Flutter:flutter_local_notifications 在前景中 运行 时不起作用

Flutter: flutter_local_notifications not work when running in Foreground

我想用 flutter_local_notifications 实现 FCM 来处理来自 backgroundforeground 的通知。我已经按照这两个文档来设置插件。当我尝试 Android 时,background 通知有效并显示通知。但是当我尝试 foreground 时,FCM 可以正常工作(发送 titlebody),但通知不显示(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");

    ...
  }

}

正如您在上面看到的,ForegroundBackground 都调用了一些函数 notificationHelper.showNormalNotification(body)。但是当它来自 Foreground 时出现上面 logcat 中的错误,而当它来自 Background 时它起作用了。

Foreground 中的 运行 时,我想设置 flutter_local_notifications 来工作?

终于,经过 2 天的工作,我找到了解决方案。

foreground 中打开应用程序或 运行 时不显示通知,这是因为在我的 AndroidManifest.xmlintent service 用于 deeplink

<intent-filter>
    ....

        <data android:scheme="${deeplink_schema}" />
</intent-filter>