How to subscribe to a firebase topic from a flutter app when the app is in background/terminated state?

45 Views Asked by At

In my app, I have a default push notification topic "DEFAULT" which is subscribed by all the user. Whenever a new topic is created, I send a push notification over "DEFAULT" topic with a data/payload property containing the new notification topic name, to all the users. The app accepts the notification and takes out the new topic from the payload. If the new topic is valid for the user then it subscribes to it, otherwise ignores it. Below is the code that is working properly only when the app is in foreground state,

static void _handleForegroundMessages() async {
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      String? topicChannel = message.data['topic'];
      if (topicChannel != null && topicChannel == PredefinedNotificationTopic.topicCreated.name) {
        String newTopic = message.data['newTopicToSubscribe'];
        FCMHelper.addToUserTopicList(newTopic);
      }
      _showLocalNotification(message);
    })
};

However, the similar code for background/terminated state is throwing exception as below

E/flutter (24845): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: [core/no-app] No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp()
E/flutter (24845): #0      MethodChannelFirebase.app (package:firebase_core_platform_interface/src/method_channel/method_channel_firebase.dart:193:5)
E/flutter (24845): #1      Firebase.app (package:firebase_core/src/firebase.dart:56:41)
E/flutter (24845): #2      FirebaseMessaging.instance (package:firebase_messaging/src/messaging.dart:32:47)
E/flutter (24845): #3      FCMService.subscribeToTopics (package:fourdrinier/service/fcm_service.dart:52:25)
E/flutter (24845): #4      FCMHelper.addToUserTopicList (package:fourdrinier/service/fcm_helper.dart:45:18)
E/flutter (24845): <asynchronous suspension>

Is it possible to achieve the same? or is there any work around?

Here is my background handler that I am calling using FirebaseMessaging.onBackgroundMessage(FCMService.backgroundHandler);

static Future<void> backgroundHandler(RemoteMessage message) async {
    String? topicChannel = message.data['topic'];
    debugPrint("Background notification with $topicChannel");
    if (topicChannel != null && topicChannel == PredefinedNotificationTopic.topicCreated.name) {
      String newTopic = message.data['newTopicToSubscribe'];
      FCMHelper.addToUserTopicList(newTopic);
    }
  }
3

There are 3 best solutions below

1
Sowat Kheang On

This function is called when the app is in background or terminated

FirebaseMessaging.onBackgroundMessage((RemoteMessage message) async {
      // TODO implement your code here
    });
1
Karan Mehta On

You can do something like this using the workmanager package when you receive a push notification then you can launch a job which performs your subscription thing :

@pragma('vm:entry-point') // Mandatory if the App is obfuscated or using Flutter 3.1+
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) {
    print("Native called background task: $task"); //simpleTask will be emitted here.
    return Future.value(true);
  });
}

void main() {
  Workmanager().initialize(
    callbackDispatcher, // The top level function, aka callbackDispatcher
    isInDebugMode: true // If enabled it will post a notification whenever the task is running. Handy for debugging tasks
  );
  Workmanager().registerOneOffTask("task-identifier", "simpleTask");
  runApp(MyApp());
}
0
BlackList96 On

It seems like the easiest solution was already mentioned in the exception message. No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp()

My issue got resolved after I added the Firebase.initializeApp() inside the background handler. Here is my final code.

static Future<void> backgroundHandler(RemoteMessage message) async {
    await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); // add this line here even if you have already added this inside main()

    String? topicChannel = message.data['topic'];
    debugPrint("Background notification with $topicChannel");
    if (topicChannel != null && topicChannel == PredefinedNotificationTopic.topicCreated.name) {
      String newTopic = message.data['newTopicToSubscribe'];
      FCMHelper.addToUserTopicList(newTopic);
    }
  }