Flutter: flutter_local_notifications not work when running in Foreground

Issue

I want to implement FCM with flutter_local_notifications to handling notification from background and foreground. I already follow both documentation to setup the plugin. And when I try on Android, the background notification is work and show the notification. But when I try on foreground, the FCM is work (send the title and body), but the notification not show (get error). The detail error is bellow:

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): 

Library Version

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");

    ...
  }

}

As you can see above, both Foreground and Background call some function notificationHelper.showNormalNotification(body). But when it’s from Foreground getting error like in the above logcat, and when from Background it’s work.

What I miss to setup flutter_local_notifications to work’s when running in Foreground?

Solution

Finally, after spend 2 days of work I found my solution.

The notification not show when app is open or running in foreground it’s because in my AndroidManifest.xml have intent service for deeplink.

<intent-filter>
    ....

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

Answered By – R Rifa Fauzi Komara

Answer Checked By – Mary Flores (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *