In Flutter, how do we use Firebase Messaging onBackgroundMessage to create a notification, using flutter_local_notifications?
Asked Answered
P

2

8

We are working on an encrypted chat application where we use Firebase Messaging for data notifications. Some client-side logic needs to be done upon receiving a data notification, before showing an actual notification to the user. For example, a phone number will have to be translated to a local contact name. This translation is done by lookup with a map that is already available globally.

The data notifications are received just fine and the onBackgroundMessage callback is called as well. However, it seems impossible to access any kind of state from the onBackgroundMessage function. For example, printing the phone number of the logged in user returns null. Printing this same global variable from the onMessage callback works just fine.

Running flutter_local_notifications from onMessage works fine, but again, does not work at all from onBackgroundMessage as 'no implementation could be found for the method .show()'. At the moment, it claims that flutterLocalNotificationsPlugin is null, which it isn't really.

It seems to us that onBackgroundMessage has no access to anything the app provides, as soon as the app is backgrounded. Something has to be done to make some of the scope/context available to the background process. For now, that would mainly be the flutter_local_notifications plugin in its entirety, as well as the local contacts list to translate phone number to name.

Has anyone got any idea how to do this?

Here is some of the code:

FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
final _chatRepository = ChatRepository();

Future<dynamic> backgroundMessageHandler(Map<String, dynamic> message) async {
  if(message.containsKey('data')) {
    await _showNotification(message);
    return Future<void>.value();
  }
}

Future _showNotification(message) async {
  List<String> numbers = [];
  numbers.add(message['data']['sender']);
  var name = await _chatRepository.translatePhoneNumbersToChatName(numbers);
  var androidPlatformChannelSpecifics = new AndroidNotificationDetails(
      'channel id', 'channel name', 'channel description',
      importance: Importance.Max, priority: Priority.High);
  var iOSPlatformChannelSpecifics = new IOSNotificationDetails();
  var platformChannelSpecifics = new NotificationDetails(
      androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
  await flutterLocalNotificationsPlugin.show(
    0,
    name,
    message['data']['body'],
    platformChannelSpecifics,
    payload: message['data']['body'],
  );
}

class NotificationHandler {
  final FirebaseMessaging fcm = FirebaseMessaging();
  StreamSubscription iosSubscription;
  String deviceToken = "";

  Future<void> initialize() async {
    flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
    var initializationSettingsAndroid =
    new AndroidInitializationSettings('@mipmap/ic_launcher');
    var initializationSettingsIOS = new IOSInitializationSettings(onDidReceiveLocalNotification: onDidReceiveLocalNotification);
    var initializationSettings = new InitializationSettings(initializationSettingsAndroid, initializationSettingsIOS);
    flutterLocalNotificationsPlugin.initialize(initializationSettings, onSelectNotification: onClickNotification);

    fcm.configure(
      onMessage: (Map<String, dynamic> message) async {
        if(message.containsKey('data')) {
          print(message);
          _showNotification(message);
        }
      },
      onBackgroundMessage: Platform.isIOS
          ? null
          : backgroundMessageHandler,
      onLaunch: (Map<String, dynamic> message) async {
        if(message.containsKey('data')) {
          print(message);
          _showNotification(message);
        }
      },
      onResume: (Map<String, dynamic> message) async {
        if(message.containsKey('data')) {
          print(message);
          _showNotification(message);
        }
      },
    );
    _updateDeviceToken();
  }
.
.
.

Of course, the initialize above is called early on in the application lifecycle.

Polyadelphous answered 6/1, 2020 at 11:24 Comment(0)
P
0

This plugin explains it all better than I could, but it just so happens that the background is a completely different isolate/context and thus it has no access to any plugins if they use an old (pre Flutter 12) API. https://pub.dev/packages/android_alarm_manager#flutter-android-embedding-v1

Embedding v1 requires you to register any plugins that you want to access from the background. Doing this makes it flutter_local_notifications work properly.

Unfortunately, FCM docs are heavily lacking.

Polyadelphous answered 8/1, 2020 at 19:6 Comment(0)
D
0
class NotificationHandler {
  static final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); // make it a static field of the class
  // ...
}

Future _showNotification(message) async {
  // ...
  await NotificationHandler.flutterLocalNotificationsPlugin.show( // access it
    // ...
  );
}

Hope this works for you.

Decani answered 6/1, 2020 at 16:13 Comment(4)
Hey, thanks for answering. I tried both your way, and something afterwards. The error that follows is this: I/flutter (20856): Unable to handle incoming background message. I/flutter (20856): MissingPluginException(No implementation found for method show on channel dexterous.com/flutter/local_notifications) So it can find the instance of FlutterLocalNotificationPlugin but it can't find its implemented methods, it seems.Polyadelphous
@Polyadelphous Have you completed the steps that are required to handle background messages? (pub.dev/packages/…)Decani
@Polyadelphous You may have forgotten to do something. I suggest you check you correctly completed the installation of flutter_local_notifications and firebase_messaging. Also, try flutter clean and flutter pub getDecani
@Polyadelphous https://mcmap.net/q/1474097/-flutter-push-notifications-even-if-the-app-is-closed did you tried this?Sejant
P
0

This plugin explains it all better than I could, but it just so happens that the background is a completely different isolate/context and thus it has no access to any plugins if they use an old (pre Flutter 12) API. https://pub.dev/packages/android_alarm_manager#flutter-android-embedding-v1

Embedding v1 requires you to register any plugins that you want to access from the background. Doing this makes it flutter_local_notifications work properly.

Unfortunately, FCM docs are heavily lacking.

Polyadelphous answered 8/1, 2020 at 19:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.