Flutter:flutter_local_notifications 在前景中 运行 时不起作用
Flutter: flutter_local_notifications not work when running in Foreground
我想用 flutter_local_notifications
实现 FCM
来处理来自 background
和 foreground
的通知。我已经按照这两个文档来设置插件。当我尝试 Android 时,background
通知有效并显示通知。但是当我尝试 foreground
时,FCM
可以正常工作(发送 title
和 body
),但通知不显示(get error
)。详细错误如下:
D/FLTFireMsgReceiver(26448): broadcast received for message
W/roonapp.stagin(26448): Accessing hidden method Landroid/os/WorkSource;->add(I)Z (greylist, reflection, allowed)
W/roonapp.stagin(26448): Accessing hidden method Landroid/os/WorkSource;->add(ILjava/lang/String;)Z (greylist, reflection, allowed)
W/roonapp.stagin(26448): Accessing hidden method Landroid/os/WorkSource;->size()I (greylist, reflection, allowed)
W/roonapp.stagin(26448): Accessing hidden method Landroid/os/WorkSource;->get(I)I (greylist, reflection, allowed)
W/roonapp.stagin(26448): Accessing hidden method Landroid/os/WorkSource;->getName(I)Ljava/lang/String; (greylist, reflection, allowed)
[log] [32m——————————————————————————————————————————————————————————————————————
DEBUG
——————————————————————————————————————————————————————————————————————
2021-12-09T22:05:18.792141
——————————————————————————————————————————————————————————————————————
Show Notification:
Title -> test0
Body -> test0
Payload -> null
——————————————————————————————————————————————————————————————————————[0m
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): Failed to handle method call
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Intent android.content.Intent.setAction(java.lang.String)' on a null object reference
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.createNotification(FlutterLocalNotificationsPlugin.java:187)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.showNotification(FlutterLocalNotificationsPlugin.java:1023)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.show(FlutterLocalNotificationsPlugin.java:1358)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.onMethodCall(FlutterLocalNotificationsPlugin.java:1240)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:233)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:84)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:865)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at android.os.MessageQueue.nativePollOnce(Native Method)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at android.os.MessageQueue.next(MessageQueue.java:336)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at android.os.Looper.loop(Looper.java:197)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at android.app.ActivityThread.main(ActivityThread.java:7948)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at java.lang.reflect.Method.invoke(Native Method)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1075)
E/flutter (26448): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: PlatformException(error, Attempt to invoke virtual method 'android.content.Intent android.content.Intent.setAction(java.lang.String)' on a null object reference, null, java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Intent android.content.Intent.setAction(java.lang.String)' on a null object reference
E/flutter (26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.createNotification(FlutterLocalNotificationsPlugin.java:187)
E/flutter (26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.showNotification(FlutterLocalNotificationsPlugin.java:1023)
E/flutter (26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.show(FlutterLocalNotificationsPlugin.java:1358)
E/flutter (26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.onMethodCall(FlutterLocalNotificationsPlugin.java:1240)
E/flutter (26448): at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:233)
E/flutter (26448): at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:84)
E/flutter (26448): at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:865)
E/flutter (26448): at android.os.MessageQueue.nativePollOnce(Native Method)
E/flutter (26448): at android.os.MessageQueue.next(MessageQueue.java:336)
E/flutter (26448): at android.os.Looper.loop(Looper.java:197)
E/flutter (26448): at android.app.ActivityThread.main(ActivityThread.java:7948)
E/flutter (26448): at java.lang.reflect.Method.invoke(Native Method)
E/flutter (26448): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
E/flutter (26448): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1075)
E/flutter (26448): )
E/flutter (26448): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:607:7)
E/flutter (26448): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:156:18)
E/flutter (26448): <asynchronous suspension>
E/flutter (26448): #2 FlutterLocalNotificationsPlugin.show (package:flutter_local_notifications/src/flutter_local_notifications_plugin.dart:194:7)
E/flutter (26448): <asynchronous suspension>
E/flutter (26448): #3 NotificationHelper.showNormalNotification (package:notification/notification/notifications_helper.dart:85:5)
E/flutter (26448): <asynchronous suspension>
E/flutter (26448):
库版本
firebase_core: ^1.10.0
firebase_messaging: ^11.2.3
firebase_analytics: ^9.0.2
flutter_local_notifications: ^9.1.4
AndroidManifest.xml
<application
...>
<activity
android:showWhenLocked="true"
android:turnScreenOn="true">
...
</activity>
...
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="channel_id_app" />
</application>
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
...
await NotificationConfig.init();
runApp(App());
}
NotificationConfig
class NotificationConfig {
static init() async {
final notificationHelper = NotificationHelper();
try {
FirebaseMessaging.onBackgroundMessage(fcmBackgroundHandler); //This work fine
} catch (e, trace) {
Logger.e('Error Running Notification in Background: $e',
ex: e, stacktrace: trace);
}
//TODO: Foreground not work (the notification)
try {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
final notification = message.notification;
if (notification != null) {
final body = ReceivedNotification(
title: notification.title,
body: notification.body,
);
notificationHelper.showNormalNotification(body); //TODO: This is error when from `foreground`
}
});
} catch (e, trace) {
Logger.e('Error Running Notification in Foreground: $e',
ex: e, stacktrace: trace);
}
}
}
NotificationHelper
Future<void> fcmBackgroundHandler(RemoteMessage message) async {
final notificationHelper = NotificationHelper();
final body = ReceivedNotification(
title: message.notification?.title,
body: message.notification?.body,
);
await notificationHelper.showNormalNotification(body); //TODO: This is not error when from `background`
}
class NotificationHelper {
/// Singleton pattern
static NotificationHelper? _instance;
NotificationHelper._internal() {
_instance = this;
_init();
}
factory NotificationHelper() =>
_instance ?? NotificationHelper._internal();
final _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
Future<void> _init() async {
await _setupLocalNotification();
await _setupFcm();
}
Future<void> _setupLocalNotification() async {
const channel = AndroidNotificationChannel(
NotificationChannel.channelId,
NotificationChannel.channelName,
description: NotificationChannel.channelDesc,
importance: Importance.max,
);
/// Initialization Settings for Android
const initializationSettingsAndroid =
AndroidInitializationSettings('@mipmap/ic_launcher');
/// Initialization Settings for iOS
const initializationSettingsIOS = IOSInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,
);
/// InitializationSettings for initializing settings for both platforms
const initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
);
await _flutterLocalNotificationsPlugin.initialize(
initializationSettings,
);
/// Create an Android Notification Channel.
///
/// We use this channel in the `AndroidManifest.xml` file to override the
/// default FCM channel to enable heads up notifications.
await _flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
}
Future<void> showNormalNotification(
ReceivedNotification? notification,
) async {
Logger.d(
'Show Notification:\n'
'Title -> ${notification?.title}\n'
'Body -> ${notification?.body}\n'
'Payload -> ${notification?.payload}\n',
);
await _flutterLocalNotificationsPlugin.show(
NotificationType.normal,
notification?.title,
notification?.body,
const NotificationDetails(
android: AndroidNotificationDetails(
NotificationChannel.channelId,
NotificationChannel.channelName,
channelDescription: NotificationChannel.channelDesc,
priority: Priority.high,
importance: Importance.max,
),
),
payload: notification?.payload,
);
}
Future<void> _setupFcm() async {
final fcm = FirebaseMessaging.instance;
await fcm.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
final token = await fcm.getToken();
Logger.d("Token FCM: $token");
...
}
}
正如您在上面看到的,Foreground
和 Background
都调用了一些函数 notificationHelper.showNormalNotification(body)
。但是当它来自 Foreground
时出现上面 logcat 中的错误,而当它来自 Background
时它起作用了。
当 Foreground
中的 运行 时,我想设置 flutter_local_notifications
来工作?
终于,经过 2 天的工作,我找到了解决方案。
在 foreground
中打开应用程序或 运行 时不显示通知,这是因为在我的 AndroidManifest.xml
中 intent service
用于 deeplink
。
<intent-filter>
....
<data android:scheme="${deeplink_schema}" />
</intent-filter>
我想用 flutter_local_notifications
实现 FCM
来处理来自 background
和 foreground
的通知。我已经按照这两个文档来设置插件。当我尝试 Android 时,background
通知有效并显示通知。但是当我尝试 foreground
时,FCM
可以正常工作(发送 title
和 body
),但通知不显示(get error
)。详细错误如下:
D/FLTFireMsgReceiver(26448): broadcast received for message
W/roonapp.stagin(26448): Accessing hidden method Landroid/os/WorkSource;->add(I)Z (greylist, reflection, allowed)
W/roonapp.stagin(26448): Accessing hidden method Landroid/os/WorkSource;->add(ILjava/lang/String;)Z (greylist, reflection, allowed)
W/roonapp.stagin(26448): Accessing hidden method Landroid/os/WorkSource;->size()I (greylist, reflection, allowed)
W/roonapp.stagin(26448): Accessing hidden method Landroid/os/WorkSource;->get(I)I (greylist, reflection, allowed)
W/roonapp.stagin(26448): Accessing hidden method Landroid/os/WorkSource;->getName(I)Ljava/lang/String; (greylist, reflection, allowed)
[log] [32m——————————————————————————————————————————————————————————————————————
DEBUG
——————————————————————————————————————————————————————————————————————
2021-12-09T22:05:18.792141
——————————————————————————————————————————————————————————————————————
Show Notification:
Title -> test0
Body -> test0
Payload -> null
——————————————————————————————————————————————————————————————————————[0m
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): Failed to handle method call
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Intent android.content.Intent.setAction(java.lang.String)' on a null object reference
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.createNotification(FlutterLocalNotificationsPlugin.java:187)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.showNotification(FlutterLocalNotificationsPlugin.java:1023)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.show(FlutterLocalNotificationsPlugin.java:1358)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.onMethodCall(FlutterLocalNotificationsPlugin.java:1240)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:233)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:84)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:865)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at android.os.MessageQueue.nativePollOnce(Native Method)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at android.os.MessageQueue.next(MessageQueue.java:336)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at android.os.Looper.loop(Looper.java:197)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at android.app.ActivityThread.main(ActivityThread.java:7948)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at java.lang.reflect.Method.invoke(Native Method)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
E/MethodChannel#dexterous.com/flutter/local_notifications(26448): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1075)
E/flutter (26448): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: PlatformException(error, Attempt to invoke virtual method 'android.content.Intent android.content.Intent.setAction(java.lang.String)' on a null object reference, null, java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Intent android.content.Intent.setAction(java.lang.String)' on a null object reference
E/flutter (26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.createNotification(FlutterLocalNotificationsPlugin.java:187)
E/flutter (26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.showNotification(FlutterLocalNotificationsPlugin.java:1023)
E/flutter (26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.show(FlutterLocalNotificationsPlugin.java:1358)
E/flutter (26448): at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.onMethodCall(FlutterLocalNotificationsPlugin.java:1240)
E/flutter (26448): at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:233)
E/flutter (26448): at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:84)
E/flutter (26448): at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:865)
E/flutter (26448): at android.os.MessageQueue.nativePollOnce(Native Method)
E/flutter (26448): at android.os.MessageQueue.next(MessageQueue.java:336)
E/flutter (26448): at android.os.Looper.loop(Looper.java:197)
E/flutter (26448): at android.app.ActivityThread.main(ActivityThread.java:7948)
E/flutter (26448): at java.lang.reflect.Method.invoke(Native Method)
E/flutter (26448): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
E/flutter (26448): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1075)
E/flutter (26448): )
E/flutter (26448): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:607:7)
E/flutter (26448): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:156:18)
E/flutter (26448): <asynchronous suspension>
E/flutter (26448): #2 FlutterLocalNotificationsPlugin.show (package:flutter_local_notifications/src/flutter_local_notifications_plugin.dart:194:7)
E/flutter (26448): <asynchronous suspension>
E/flutter (26448): #3 NotificationHelper.showNormalNotification (package:notification/notification/notifications_helper.dart:85:5)
E/flutter (26448): <asynchronous suspension>
E/flutter (26448):
库版本
firebase_core: ^1.10.0
firebase_messaging: ^11.2.3
firebase_analytics: ^9.0.2
flutter_local_notifications: ^9.1.4
AndroidManifest.xml
<application
...>
<activity
android:showWhenLocked="true"
android:turnScreenOn="true">
...
</activity>
...
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="channel_id_app" />
</application>
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
...
await NotificationConfig.init();
runApp(App());
}
NotificationConfig
class NotificationConfig {
static init() async {
final notificationHelper = NotificationHelper();
try {
FirebaseMessaging.onBackgroundMessage(fcmBackgroundHandler); //This work fine
} catch (e, trace) {
Logger.e('Error Running Notification in Background: $e',
ex: e, stacktrace: trace);
}
//TODO: Foreground not work (the notification)
try {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
final notification = message.notification;
if (notification != null) {
final body = ReceivedNotification(
title: notification.title,
body: notification.body,
);
notificationHelper.showNormalNotification(body); //TODO: This is error when from `foreground`
}
});
} catch (e, trace) {
Logger.e('Error Running Notification in Foreground: $e',
ex: e, stacktrace: trace);
}
}
}
NotificationHelper
Future<void> fcmBackgroundHandler(RemoteMessage message) async {
final notificationHelper = NotificationHelper();
final body = ReceivedNotification(
title: message.notification?.title,
body: message.notification?.body,
);
await notificationHelper.showNormalNotification(body); //TODO: This is not error when from `background`
}
class NotificationHelper {
/// Singleton pattern
static NotificationHelper? _instance;
NotificationHelper._internal() {
_instance = this;
_init();
}
factory NotificationHelper() =>
_instance ?? NotificationHelper._internal();
final _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
Future<void> _init() async {
await _setupLocalNotification();
await _setupFcm();
}
Future<void> _setupLocalNotification() async {
const channel = AndroidNotificationChannel(
NotificationChannel.channelId,
NotificationChannel.channelName,
description: NotificationChannel.channelDesc,
importance: Importance.max,
);
/// Initialization Settings for Android
const initializationSettingsAndroid =
AndroidInitializationSettings('@mipmap/ic_launcher');
/// Initialization Settings for iOS
const initializationSettingsIOS = IOSInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,
);
/// InitializationSettings for initializing settings for both platforms
const initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
);
await _flutterLocalNotificationsPlugin.initialize(
initializationSettings,
);
/// Create an Android Notification Channel.
///
/// We use this channel in the `AndroidManifest.xml` file to override the
/// default FCM channel to enable heads up notifications.
await _flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
}
Future<void> showNormalNotification(
ReceivedNotification? notification,
) async {
Logger.d(
'Show Notification:\n'
'Title -> ${notification?.title}\n'
'Body -> ${notification?.body}\n'
'Payload -> ${notification?.payload}\n',
);
await _flutterLocalNotificationsPlugin.show(
NotificationType.normal,
notification?.title,
notification?.body,
const NotificationDetails(
android: AndroidNotificationDetails(
NotificationChannel.channelId,
NotificationChannel.channelName,
channelDescription: NotificationChannel.channelDesc,
priority: Priority.high,
importance: Importance.max,
),
),
payload: notification?.payload,
);
}
Future<void> _setupFcm() async {
final fcm = FirebaseMessaging.instance;
await fcm.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
final token = await fcm.getToken();
Logger.d("Token FCM: $token");
...
}
}
正如您在上面看到的,Foreground
和 Background
都调用了一些函数 notificationHelper.showNormalNotification(body)
。但是当它来自 Foreground
时出现上面 logcat 中的错误,而当它来自 Background
时它起作用了。
当 Foreground
中的 运行 时,我想设置 flutter_local_notifications
来工作?
终于,经过 2 天的工作,我找到了解决方案。
在 foreground
中打开应用程序或 运行 时不显示通知,这是因为在我的 AndroidManifest.xml
中 intent service
用于 deeplink
。
<intent-filter>
....
<data android:scheme="${deeplink_schema}" />
</intent-filter>