Solvedflutterfire Unable to use onBackgroundMessage in Firebase Cloud Messaging

I am trying to implement an app with notifications. I followed the official instructions of firebase_messaging (https://pub.dev/packages/firebase_messaging), and everything worked as promissed, but there is something I don't understand. I was able to trigger onMessage, onLaunch and onResume and make the UI respond properly (showing the user the recieved message). But when onBackgroundMessage is triggered, I can only print the message to the debugging console. I looked all over the internet, but it seems like that's how all discussions end. What can I do inside onBackgroundMessage? I failed to find any way to show to the user that the message had been recieved. I tried to navigate using a global navigator key but navigatorKey.currentState is null while running in background. I tried to update a provider, but it was also null. I even tried to update a global variable but it was the old value again when the app resumed to foreground. Could anyone please publish code example in which onBackgroundMessage has any impact on the user? (not only printing to the console).

The reason that I really want to use onBackgroundMessage is because when I get the notification while running in background, onResume and onLaunch are triggered only if I click the notification. If I click the app icon, the notification stays and nothing is triggered. I have no way to tell the user about the notification. If someone finds a way to read notifications that the user didn't click yet, that would help even more.

This is my code. When pressing button 1, a notification is sent after a delay of 5 seconds (to let the user time to put the app in background). When pressing button 2, a data message is sent.
Uppon recieving the message the three counters are incremented. But in some cases nothing happens.

expected result
In all scenarios all three counters should increment.

Actual result
In case the app was running in background while the message was sent, after resuming to the app nothing happend (both with buttom 1 and 2).

import 'dart:convert';

import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() => runApp(MyApp());

class MyProvider extends StatefulWidget {
  final Widget child;
  const MyProvider({Key key, this.child}) : super(key: key);

  @override
  MyProviderState createState() => MyProviderState();
}

class MyProviderState extends State<MyProvider> {
  int counter = 0;

  void increment() {
    setState(() {
      counter += 1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Provider.value(
      value: counter,
      child: Provider.value(
        value: this,
        child: widget.child,
      ),
    );
  }
}

const String serverToken = /* put your own server token here */;
final FirebaseMessaging firebaseMessaging = FirebaseMessaging();

int globalInt = 0;
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
final GlobalKey<MyProviderState> providerKey = GlobalKey<MyProviderState>();

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('Rebuild MyApp');
    SystemChrome.setPreferredOrientations([
      DeviceOrientation.portraitUp,
      DeviceOrientation.portraitDown,
    ]);
    SharedPreferences.getInstance().then((prefs) => prefs.setString('counter3', '0'));
    return MyProvider(
      key: providerKey,
      child: MaterialApp(
        title: 'Flutter Demo',
        navigatorKey: navigatorKey,
        home: HomePage(),
        routes: {},
      ),
    );
  }
}

Future<void> sendNotification(int buttonNumber) async {
  print('Pressed button $buttonNumber');
  // This delay is to give the user time to move the app to background.
  await Future.delayed(Duration(seconds: 5));
  await http.post(
    'https://fcm.googleapis.com/fcm/send',
    headers: <String, String>{
      'Content-Type': 'application/json',
      'Authorization': 'key=$serverToken',
    },
    body: jsonEncode(
      <String, dynamic>{
        'notification': buttonNumber == 1
            ? <String, dynamic>{
                'body': 'This is a body',
                'title': 'This is a title',
              }
            : <String, dynamic>{},
        'priority': 'high',
        'data': <String, dynamic>{
          'click_action': 'FLUTTER_NOTIFICATION_CLICK',
          'id': '1',
          'status': 'done',
          'action': buttonNumber,
        },
        'to': await firebaseMessaging.getToken(),
      },
    ),
  );
  print('Sent notification');
}

Future<void> handleNotification(Map<String, dynamic> message) async {
  int action = int.parse(message['data']['action']);
  print('>>> Got notification with action $action');
  print('>>> navigatorKey.currentState = ${navigatorKey.currentState}');
  print('>>> providerKey.currentState = ${providerKey.currentState}');
  if (navigatorKey.currentState != null) {
    print('>>> You are lucky! The app is in foreground or the user clicked the notification.');
    print('>>> You can update the UI or navigate to inform the user of the notification.');
  } else {
    print(
        '>>> You are helpless! onBackgroundMessage was triggered but there is nothing you can do to inform the user.');
  }
  // Increment counter1
  print('>>> trying to increment counter1 from $globalInt to ${globalInt + 1}');
  globalInt += 1;
  // Increment counter2
  try {
    print('>>> trying to increment counter2');
    providerKey.currentState.increment();
  } catch (e) {
    print('>>> Error: <<$e>>');
  }
  // Increment counter3
  try {
    print('>>> trying to increment counter3');
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final int value = int.parse(prefs.getString('counter3'));
    prefs.setString('counter3', '${value + 1}');
  } catch (e) {
    print('>>> Error: <<$e>>');
  }
}

Future<void> onBackgroundMessage(Map<String, dynamic> message) async {
  print('>>> onBackgroundMessage');
  await handleNotification(message);
}

Future<void> onMessage(Map<String, dynamic> message) async {
  print('>>> onMessage');
  await handleNotification(message);
}

Future<void> onResume(Map<String, dynamic> message) async {
  print('>>> onResume');
  await handleNotification(message);
}

Future<void> onLaunch(Map<String, dynamic> message) async {
  print('>>> onLaunch');
  await handleNotification(message);
}

class HomePage extends StatefulWidget {
  @override
  _HomePage createState() => _HomePage();
}

class _HomePage extends State<HomePage> {
  int counter1 = 0;
  int counter2 = 0;
  int counter3 = 0;

  @override
  void initState() {
    super.initState();
    firebaseMessaging.requestNotificationPermissions(
        const IosNotificationSettings(sound: true, badge: true, alert: true, provisional: false));
    firebaseMessaging.configure(
      onBackgroundMessage: onBackgroundMessage,
      onMessage: onMessage,
      onResume: onResume,
      onLaunch: onLaunch,
    );
  }

  @override
  Widget build(BuildContext context) {
    counter1 = globalInt;
    counter2 = Provider.of<int>(context);
    SharedPreferences.getInstance().then((prefs) {
      setState(() {
        counter3 = int.parse(prefs.getString('counter3'));
      });
    });
    return Scaffold(
      body: Column(
        children: [
          Expanded(
            child: SizedBox(height: 50),
          ),
          Expanded(
            child: Text('$counter1', textScaleFactor: 3.5),
          ),
          Expanded(
            child: Text('$counter2', textScaleFactor: 3.5),
          ),
          Expanded(
            child: Text('$counter3', textScaleFactor: 3.5),
          ),
          Expanded(
            child: Row(children: [
              Expanded(
                child: FloatingActionButton(
                  onPressed: () => sendNotification(1),
                  child: Text('1', textScaleFactor: 2),
                ),
              ),
              Expanded(
                child: FloatingActionButton(
                  onPressed: () => sendNotification(2),
                  child: Text('2', textScaleFactor: 2),
                ),
              ),
            ]),
          ),
        ],
      ),
    );
  }
}
52 Answers

βœ”οΈAccepted Answer

Finally I got something working! I ended up using a named ReceivePort.

Here is what I did in main.dart:

final ReceivePort backgroundMessageport = ReceivePort();
const String backgroundMessageIsolateName = 'fcm_background_msg_isolate';

Future<dynamic> backgroundMessageHandler(Map<String, dynamic> message) async {
  if (message.containsKey('data')) {
    final port = IsolateNameServer.lookupPortByName(backgroundMessageIsolateName);
    port.send(message);
  }
}

void backgroundMessagePortHandler(message) {
  final dynamic data = message['data'];

  // Here I can access and update my top-level variables.
}

void main() {
 ...

 IsolateNameServer.registerPortWithName(
   backgroundMessageport.sendPort,
   backgroundMessageIsolateName,
 );

 backgroundMessageport.listen(backgroundMessagePortHandler);

 runApp(...)
}

Not sure if this violates Flutter or/and the isolate pattern and I would be happy to get input on this!

Other Answers:

@dvdlg Hi, i had found a solution for this issue yesterday... You should know that backgroundMessage Handler Method handle only Data message, this mean your message should not the message that your sending should have this structure : {
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"data":{
"Nick" : "Mario",
"body" : "great match!",
"Room" : "PortugalVSDenmark"
}
}
}
You should not put notification in it

@dvdlg

@mostafa5731 , thank you very much for your answer, but unfortunately, it didn't work. When I tried to follow the steps in the link that you sent I got:

ERROR: MissingPluginException(No implementation found for method show on channel dexterous.com/flutter/local_notifications)

moreover, this solution doesn't seem to answer my question. I want to display to the user how many messages he has got so far. For this I need to somehow change the app's state (change a global variable or the disk memory or anything), but it seems that background code cannot handle such thing.
I would be fine with onResume (without onBackgroundMessage) if it could be triggered when the user resumes to the app without tapping the notification.

class YourApplication : FlutterApplication(), PluginRegistrantCallback {
    override fun onCreate() {
        super.onCreate()
        FlutterFirebaseMessagingService.setPluginRegistrant(this);
    }

    override fun registerWith(registry: PluginRegistry) {
        CustomPluginRegistrant.registerWith(registry)
    }
}
class CustomPluginRegistrant {
    companion object {
        fun registerWith(registry: PluginRegistry) {
            if (alreadyRegisteredWith(registry)) {
                return
            }
            FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"))
            FlutterLocalNotificationsPlugin.registerWith(registry.registrarFor("com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin"));
        }

        fun alreadyRegisteredWith(registry: PluginRegistry): Boolean {
            val key: String = FirebaseCloudMessagingPluginRegistrant::class.java.getCanonicalName()
            if (registry.hasPlugin(key)) {
                return true
            }
            registry.registrarFor(key)
            return false
        }
    }
}

Related Issues:

381
flutterfire [firebase_core_web , firebase_auth_web, cloud_firestore_web ] Plugin project :firebase_auth_web not found. Please update settings.gradle.
Hi @gsusI Please add this in flutter app -> android -> settings.gradle if you disagree please write ...
60
flutterfire [FIREBASE_AUTH] 'UserAgent.h' file not found flutter firebase iOS
I changed dependencies try this Don't use ^ in version. I am working on flutter application with fir...
59
flutterfire [firebase_messaging] <Background push notification handling - no implementation found for method FcmDartService#initialized>
So in the new firebase_messaging version support for background messages was added Actually if you d...
57
flutterfire [firebase_auth] iOS verifyPhoneNumber Token mismatch
Same problem with firebase_auth: 0.15.5+3: on the simulator I get the CAPTCHA code and it works ...
37
flutterfire [firebase_auth] iOS doesn't build when upgrade to 1.15.x
I fixed this temporarily by running pod update in ios folder Describe the bug Flutter app doesn't bu...
36
flutterfire πŸ› [firebase_auth] Incompatible with latest flutter_localizations master channel.
For now you can simply add the following to your pubspec.yaml if you need to stay on dev channel and...
34
flutterfire [cloud_firestore] no visible @interface for 'FIRQuery' declares the selector ...
cd ios and run pod update Firebase/Firestore and run build command let me know ...
32
flutterfire [firebase_messaging] Application.kt: Type mismatch: inferred type is PluginRegistry but FlutterEngine was expected
There is a workaround mentioned in: #1613 Replace: To: Also temporary downgrade firebase_messaging t...
31
flutterfire Firebase messaging onResume and onMessage broken on iOS since Flutter 1.7.4
everybody: What would be possible other implications when removing that code? Removing those lines m...
29
flutterfire [firebase_auth] Method swizzling problem with phone auth iOS
@cielo Hi Yes i did I got Token mismatch too But when i fixed AppDelegate.swift i solved this error ...
29
flutterfire [firebase_admob 0.11.0+1] XCode Build Fails: Google-Mobile-Ads-SDK-fjgkwjemgangjofelixrcilvndtg
Hey guys I think this is because AdMob recently updated their SDK to 8.0.0(because of this firebase_...
28
flutterfire [firebase_messaging] Type mismatch: inferred type is PluginRegistry? but FlutterEngine was expected
Fixed by replace with: Following the instructions on pub.dev for handling background messages doesn'...
28
flutterfire [firebase_messaging] Android v2 embedding crashes
Why is this not labelled severe? This causes crash dialogs to show periodically when the application...
26
flutterfire πŸ› [firebase_messaging][firebase_auth] Data-only messages not received in iOS. Never fires anything in onMessage
Disabling method swizzling and using the below in my AppDelegate.swift Bug report Describe the bug O...
23
flutterfire πŸ› [firebase_remote_config: 0.4.1] Plugin using removed method, causing app to crash
Fixed for me by using implementation platform('com.google.firebase:firebase-bom') instead of impleme...
22
flutterfire [firebase_messaging] The application cannot be created when I add the Aplication.java
Hi I want to use firebase notifications in my application I'm adding Aplication.java for this After ...
22
flutterfire πŸ› [Crashlytics] iOS build crashes when adding crashlytics
@kuromukira Workaround for Flutter 2.0.4 is to use firebase_crashlytics from git instead of pub.dev ...
21
flutterfire [Cloud Firestore] fatal error: module 'cloud_firestore' not found
I think I managed to workaround the issue I got this error as well after integrating cocoapods mysel...
20
flutterfire Warnings from the cloud_firestore plugin on Android: FirebaseCorePlugin.java uses unchecked or unsafe operations
set: multiDexEnabled true solved for me project/android/app/build.gradle: Steps to Reproduce Create ...
18
flutterfire firebase-messaging: Android Integration
@OliverWangWei Sorry.. It was a mistake to edit the MainActivity.kr file Android Integration I'm not...
18
flutterfire [firebase_crashlytics] Either the Crashlytics plugin doesn't work, or the instruction is wrong
Having the same issue even with a really simple app: I followed the setup instruction from Pub: http...
18
flutterfire πŸ› [firebase_core] java.lang.IllegalArgumentException: Service not registered: com.google.android.gms.measurement.internal.zzji@dbed45a
I get the same error as @kw2019ltd : I get it in a new created flutter project with the only depende...
17
flutterfire [firebase_messaging] version 6.0.7 brokes builds
I solved it. Adding the plugin dependencies in app/build.gradle My project cannot be built or runned...
16
flutterfire [all] Flutter Android build fails with firebase dependencies.
Hey @erluxman I am using Android Studio 3.5 stable java -version 1.8 and this is the issue : I have ...
16
flutterfire [cloud_firestore] INTERNAL ASSERTION FAILED: A transaction object cannot be used after its update callback has been invoked.
Tracked this down to Firebase offline use and Transactions Describe the bug App crash when adding ne...
16
flutterfire [firebase_admob] ads invades safe area and close button is blocked on iPhone X
Having the same problem Temporary solution: Hide status bar when ads are shown Hope this issue will ...
14
flutterfire [firebase_messaging] iOS notification onResume, onLaunch callback not working
@AlexandreRoba @SandyAra same issue exists or your comment solution working fine on firebase_messagi...
14
13
flutterfire [firebase_messaging] No documentation for Android v2 embedding backgroundMessage handler
Solved it with following: app/src/build.gradle add the following: implementation com.google.firebase...
13
flutterfire Unable to use onBackgroundMessage in Firebase Cloud Messaging
Finally I got something working! I ended up using a named ReceivePort I am trying to implement an ap...
13
flutterfire πŸ› [CLOUD_FIRESTORE] The service is currently unavailable.
Keeps happening...!! what is going on ? Bug report I'm try to use Firestore with Flutter Deskop (Mac...
12
flutterfire [firebase_in_app_messaging] Can't build for iOS after adding firebase_in_app_messaging
I found out how to fix this Initially Describe the bug The XCode build fails when it's processing th...
8
flutterfire πŸ› [firebase_messaging] iOS Notifications not received in 8.0.0-dev.8
I'm afraid to say that iOS is still completely dead for me on dev.9 Bug report Describe the bug Afte...
7
flutterfire πŸ› [Firebase_auth] Not persisting user on authStateChanges stream after 1.1.2
I agree with @budo385 My app was working fine with firebase_auth 1.1.0 version ...
7
flutterfire [firebase_auth] The reload() function on a FirebaseUser doesn't seem to be working.
Please fix this Hey when calling the .reload() function on a FirebaseUser nothing seems to happen ...
6
flutterfire πŸ› [firebase_crashlytics] Failed to retrieve settings from https://firebase-settings.crashlytics.com
May be it's a 24 hours patience game or try the solution below: Few changes made were: Add implement...
5
flutterfire [<Firebase_Auth>] Unhandled Platform Exception Before Able to Catch SignInWithEmailAndPassword FirebaseAuthException
I am able to replicate this issue on latest stable (1.20.3) while debugging signInWithEmailAndPasswo...
3
flutterfire [Firebase Messaging] onBackgroundMessage Never Called
this is how i have created a Notification Handler Class I followed the tutorial on how to add the li...
3
flutterfire [firebase_messaging] firebase_messaging plugin causing build error in android
@DagobertDokate comment out firebase_messaging: ^5.1.5 run flutter pub get then add firebase_messagi...
3
flutterfire Flutter heads up notification for Android
@abdallahshaban557 @vipinnegi90 For Now use flutter_local_notification to create a notification chan...
3
flutterfire πŸ› [Firebase_Auth] Auth Blocking iOS firebase_messaging dev callbacks
Have the same problem on iOS πŸ˜• Bug report Describe the bug didReceiveRemoteNotification is used in ...
3
flutterfire πŸ› [firebase_messaging] Can't use 3rd party plugins in background handler
@asmodeoux @markusaksli-nc Allright I'm back with a workaround Bug report Describe the bug None of 3...
459
flutter Multiple commands produce '/build/ios/Debug-iphonesimulator/Runner.app/Frameworks/Flutter.framework
I was able to fix it by opening the Runner workspace project in Xcode 10 Then navigate to File ...
449
flutter App Store iOS submission fails: The bundle Runner.app/Frameworks/App.framework does not support the minimum OS Version specified in the Info.plist
After doing a flutter clean changing MinimumOSVersion (inside /ios/Flutter/AppframeworkInfo.plist) t...
418
flutter 'com.android.support:appcompat-v7' has different version for the compile (26.1.0) and runtime (27.0.1) classpath
is like this i receive an error when i run flutter run My gradle.build file is: flutter doctor: ...
409
flutter Why does my text not wrap?
You have to wrap Column in a flexible Without flexible the column's width is not constrained to the ...
383
flutter Unexpected top padding in ListView put inside scaffold with no appBar
Yeah this is intentional If you put a widget before the ListView you should wrap the ListView with a...