Flutter通知监听插件flutter_notification_listener的使用
Flutter通知监听插件 flutter_notification_listener
的使用
简介
flutter_notification_listener
是一个 Flutter 插件,用于监听 Android 设备上的所有传入通知。该插件通过启动一个服务来监听通知,并提供了简单的方法来访问通知的字段。此外,它还支持在后台执行 Dart 代码,并在设备重启后自动启动服务。
特性
- 服务:启动一个服务来监听通知。
- 简单:轻松访问通知的字段。
- 后台运行:在后台执行 Dart 代码,并在设备重启后自动启动服务。
- 交互式:通知在 Flutter 中是可交互的。
安装
1. 修改 pubspec.yaml
在你的 pubspec.yaml
文件中添加 flutter_notification_listener
依赖:
dependencies:
flutter_notification_listener: ^<latest_version>
然后安装依赖:
- 从终端运行:
flutter pub get
- 从 Android Studio/IntelliJ:点击
pubspec.yaml
顶部的Packages get
- 从 VS Code:点击
pubspec.yaml
顶部的Get Packages
2. 注册服务和权限
在 AndroidManifest.xml
文件中注册服务和权限:
<service android:name="im.zoe.labs.flutter_notification_listener.NotificationsHandlerService"
android:label="Flutter Notifications Handler"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
快速开始
1. 初始化插件并添加监听处理器
import 'package:flutter/material.dart';
import 'package:flutter_notification_listener/flutter_notification_listener.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: NotificationsLog(),
);
}
}
class NotificationsLog extends StatefulWidget {
@override
_NotificationsLogState createState() => _NotificationsLogState();
}
class _NotificationsLogState extends State<NotificationsLog> {
List<NotificationEvent> _log = [];
bool started = false;
bool _loading = false;
ReceivePort port = ReceivePort();
@override
void initState() {
initPlatformState();
super.initState();
}
@pragma('vm:entry-point') // 防止 Flutter 在发布构建时剥离此函数
static void _callback(NotificationEvent evt) {
print("send evt to ui: $evt");
final SendPort? send = IsolateNameServer.lookupPortByName("_listener_");
if (send == null) print("can't find the sender");
send?.send(evt);
}
Future<void> initPlatformState() async {
NotificationsListener.initialize(callbackHandle: _callback);
IsolateNameServer.removePortNameMapping("_listener_");
IsolateNameServer.registerPortWithName(port.sendPort, "_listener_");
port.listen((message) => onData(message));
var isRunning = (await NotificationsListener.isRunning) ?? false;
print("Service is ${!isRunning ? "not " : ""}already running");
setState(() {
started = isRunning;
});
}
void onData(NotificationEvent event) {
setState(() {
_log.add(event);
});
print(event.toString());
}
void startListening() async {
print("start listening");
setState(() {
_loading = true;
});
var hasPermission = (await NotificationsListener.hasPermission) ?? false;
if (!hasPermission) {
print("no permission, so open settings");
NotificationsListener.openPermissionSettings();
return;
}
var isRunning = (await NotificationsListener.isRunning) ?? false;
if (!isRunning) {
await NotificationsListener.startService(
foreground: false,
title: "Listener Running",
description: "Welcome to having me");
}
setState(() {
started = true;
_loading = false;
});
}
void stopListening() async {
print("stop listening");
setState(() {
_loading = true;
});
await NotificationsListener.stopService();
setState(() {
started = false;
_loading = false;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Listener Example'),
actions: [
IconButton(
onPressed: () {
print("TODO:");
},
icon: Icon(Icons.settings))
],
),
body: Center(
child: ListView.builder(
itemCount: _log.length,
reverse: true,
itemBuilder: (BuildContext context, int idx) {
final entry = _log[idx];
return ListTile(
onTap: () {
entry.tap();
},
title: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(entry.title ?? "<<no title>>"),
Text(entry.text ?? "<<no text>>"),
Row(
children: (entry.actions ?? []).map((act) {
return TextButton(
onPressed: () {
if (act.semantic == 1) {
Map<String, dynamic> map = {};
(act.inputs ?? []).forEach((e) {
map[e.resultKey ?? 'null'] =
"Auto reply from me";
});
act.postInputs(map);
} else {
act.tap();
}
},
child: Text(act.title ?? ''));
}).toList()
..add(TextButton(
child: Text("Full"),
onPressed: () async {
try {
var data = await entry.getFull();
print("full notifaction: $data");
} catch (e) {
print(e);
}
})),
),
Text(entry.createAt.toString().substring(0, 19)),
],
),
));
})),
floatingActionButton: FloatingActionButton(
onPressed: started ? stopListening : startListening,
tooltip: 'Start/Stop sensing',
child: _loading
? Icon(Icons.close)
: (started ? Icon(Icons.stop) : Icon(Icons.play_arrow)),
),
);
}
}
使用说明
1. 启动服务后自动重启
在 AndroidManifest.xml
中注册广播接收器:
<receiver android:name="im.zoe.labs.flutter_notification_listener.RebootBroadcastReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
添加必要的权限:
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
2. 在后台线程执行任务
定义你自己的回调函数来处理传入的通知:
@pragma('vm:entry-point')
static void _callback(NotificationEvent evt) {
// 持久化数据
db.save(evt);
// 如果需要,将事件发送到 UI 线程
print("send evt to ui: $evt");
final SendPort send = IsolateNameServer.lookupPortByName("_listener_");
if (send == null) print("can't find the sender");
send?.send(evt);
}
在初始化时注册处理程序:
Future<void> initPlatformState() async {
NotificationsListener.initialize(callbackHandle: _callback);
}
在 UI 线程中监听事件(如果需要):
void onData(NotificationEvent event) {
print(event.toString());
}
Future<void> initPlatformState() async {
// ...
NotificationsListener.receivePort.listen((evt) => onData(evt));
}
3. 更改监听服务的通知
在启动监听服务之前,可以设置一些参数:
await NotificationsListener.startService({
bool foreground = true, // 使用 false 将不会提升到前台并且没有通知
String title = "Change the title",
String description = "Change the text",
});
4. 点击通知
在通知到达时自动点击通知:
void onData(NotificationEvent event) {
print(event.toString());
if (event.canTap) event.tap();
}
5. 点击通知的操作
与通知中的操作进行交互:
void onData(NotificationEvent event) {
print(event.toString());
event.actions.forEach((act) {
if (act.semantic == 2) { // 语义代码为 2 表示这是一个忽略操作
act.tap();
}
});
}
6. 回复对话
自动回复通知中的对话:
void onData(NotificationEvent event) {
print(event.toString());
event.actions.forEach((act) {
if (act.semantic == 1) { // 语义代码为 1 表示快速回复
Map<String, dynamic> map = {};
act.inputs.forEach((e) {
map[e.resultKey] = "Auto reply from flutter";
});
act.postInputs(map);
}
});
}
API 参考
对象 NotificationEvent
uniqueId
:String
, 通知的唯一 ID。key
:String
, 状态栏通知的键。packageName
:String
, 发布通知的应用程序的包名。uid
:int
, 通知的用户 ID。channelId
:String
, 通知的通道 ID。id
:int
, 通知的 ID。createAt
:DateTime
, 通知在 Flutter 侧的创建时间。timestamp
:int
, 通知的发布时间。title
:String
, 通知的标题。text
:String
, 通知的文本。hasLargeIcon
:bool
, 通知是否有大图标。largeIcon
:Uint8List
, 通知的大图标。canTap
:bool
, 通知是否有内容待处理意图。raw
:Map<String, dynamic>
, 通知的原始数据。
方法:
Future<bool> tap()
: 点击通知,通常会清除通知。Future<dynamic> getFull()
: 获取 Android 侧的完整通知对象。
对象 Action
id
:int
, 动作在动作数组中的索引。title
:String
, 动作的标题。semantic
:int
, 动作的语义类型。inputs
:ActionInput
, 动作的输入列表。
语义类型:
SEMANTIC_ACTION_ARCHIVE = 5;
SEMANTIC_ACTION_CALL = 10;
SEMANTIC_ACTION_DELETE = 4;
SEMANTIC_ACTION_MARK_AS_READ = 2;
SEMANTIC_ACTION_MARK_AS_UNREAD = 3;
SEMANTIC_ACTION_MUTE = 6;
SEMANTIC_ACTION_NONE = 0;
SEMANTIC_ACTION_REPLY = 1;
SEMANTIC_ACTION_THUMBS_DOWN = 9;
SEMANTIC_ACTION_THUMBS_UP = 8;
SEMANTIC_ACTION_UNMUTE = 7;
方法:
Future<bool> tap()
: 点击通知的动作。Future<bool> postInputs(Map<String, dynamic> map)
: 向通知发送输入数据。
对象 ActionInput
label
:String
, 输入的标签。resultKey
:String
, 输入的结果键。
类 NotificationsListener
字段:
isRunning
:bool
, 检查监听服务是否正在运行。hasPermission
:bool
, 检查是否授予了启动监听服务的权限。receivePort
:ReceivePort
, 默认的事件接收端口。
静态方法:
Future<void> initialize()
: 初始化插件。Future<void> registerEventHandle(EventCallbackFunc callback)
: 注册事件处理程序。Future<void> openPermissionSettings()
: 打开系统监听通知权限设置页面。Future<bool?> startService({...})
: 启动监听服务。Future<bool?> stopService()
: 停止监听服务。Future<void> promoteToForeground({...})
: 将服务提升到前台。Future<void> demoteToBackground()
: 将服务降级到后台。
已知问题
- 如果服务不是前台服务,服务在重启后将无法启动。
支持
如果你觉得这个插件有用,请考虑捐款以帮助改进它!
贡献
欢迎贡献!
更多关于Flutter通知监听插件flutter_notification_listener的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter通知监听插件flutter_notification_listener的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何使用 flutter_notification_listener
插件的示例代码。这个插件允许你监听 Android 和 iOS 上的本地通知事件。首先,你需要确保你的 Flutter 项目中已经添加了 flutter_notification_listener
依赖。
- 在
pubspec.yaml
文件中添加依赖:
dependencies:
flutter:
sdk: flutter
flutter_notification_listener: ^x.y.z # 请替换为最新版本号
-
运行
flutter pub get
来获取依赖。 -
配置 Android 和 iOS 通知权限(这里只展示 Android 配置,iOS 配置类似):
-
在
android/app/src/main/AndroidManifest.xml
中添加通知权限:<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.VIBRATE"/>
-
确保你的应用具有处理通知的权限,通常在
MainActivity.kt
或MainActivity.java
中配置。
-
-
在 Dart 代码中使用
flutter_notification_listener
:
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_notification_listener/flutter_notification_listener.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: NotificationListenerScreen(),
);
}
}
class NotificationListenerScreen extends StatefulWidget {
@override
_NotificationListenerScreenState createState() => _NotificationListenerScreenState();
}
class _NotificationListenerScreenState extends State<NotificationListenerScreen> {
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
FlutterNotificationListener? flutterNotificationListener;
@override
void initState() {
super.initState();
// 初始化 FlutterLocalNotificationsPlugin
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('app_icon');
const IOSInitializationSettings initializationSettingsIOS = IOSInitializationSettings();
const InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: (String? payload) async {
if (payload != null) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Payload:'),
content: Text(payload),
actions: <Widget>[
TextButton(
child: Text('Ok'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
});
}
});
// 初始化 FlutterNotificationListener
flutterNotificationListener = FlutterNotificationListener(
onSelectNotification: (String? payload) async {
print("Notification clicked with payload: $payload");
},
onCancelNotification: (String? id) async {
print("Notification cancelled with id: $id");
},
onBackgroundMessage: (Map<String, dynamic> message) async {
print("Received background message: $message");
},
);
// 配置通知监听
flutterNotificationListener?.initialize();
}
@override
void dispose() {
flutterNotificationListener?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Notification Listener Example'),
),
body: Center(
child: ElevatedButton(
onPressed: () async {
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
'your_channel_id',
'Your Channel Name',
'Your Channel Description',
importance: Importance.max,
priority: Priority.high,
);
const NotificationDetails platformChannelSpecifics = NotificationDetails(
android: androidPlatformChannelSpecifics, iOS: null);
await flutterLocalNotificationsPlugin.show(
0, 'plain title', 'plain body', platformChannelSpecifics,
payload: 'item x');
},
child: Text('Show Notification'),
),
),
);
}
}
注意:
flutter_notification_listener
和flutter_local_notifications
是两个插件,但在这个示例中,我们使用了flutter_local_notifications
来发送通知,因为flutter_notification_listener
专注于监听通知事件,而不是发送通知。- 确保你正确配置了 Android 和 iOS 的通知权限和通道。
- 在真实应用中,处理后台消息时,你可能需要在
iOS
项目中配置AppDelegate
和BackgroundMessageHandler
。
这段代码展示了如何初始化 flutter_local_notifications
和 flutter_notification_listener
插件,并发送一个本地通知,同时监听通知的点击和取消事件。