Flutter - FCM: how to store background message to SQLite

2,812

For some reason onBackgroundMessage is only called when the FCM message does not contain a notification (e.g. is a pure data message). Messages like that will of cause not trigger a notification on the device by default.

What I ended up doing is only send data messages (to be able to catch them in background) and handle the notification part on the device myself with the flutter_local_modifications packages.

// TOP-LEVEL or STATIC function to handle background messages
static Future<dynamic> onBackgroundMessage(
    Map<String, dynamic> notification) async {

  var msg;
  if (Platform.isAndroid) {
    msg = notification['data'];
  } else {
    msg = notification;
  }

  DatabaseService _dbService = DatabaseService();
  msg = await _dbService.insertMessage(msg);
  _showNotification(msg);
  return Future<void>.value();
}

static Future _showNotification(Map<String, dynamic> msg) async {
  // TODO: Use a proper channel
  var platformChannelSpecificsAndroid = AndroidNotificationDetails(
      'your channel id', 'your channel name', 'your channel description',
      importance: Importance.Max, priority: Priority.High);
  var platformChannelSpecificsIos =
      IOSNotificationDetails(presentSound: true);
  var platformChannelSpecifics = NotificationDetails(
      platformChannelSpecificsAndroid, platformChannelSpecificsIos);

  return Future.delayed(Duration.zero, () {
    _flutterLocalNotificationsPlugin.show(
      "some id",
      msg["title"],
      msg["body"],
      platformChannelSpecifics,
      payload: "some id",
    );
  });
}

For this to work, all involved plugins must be registered early like described below "By default background messaging is not enabled. To handle messages in the background" in firebase_messaging.

Trying to translate what is written there to a kotlin project I ended up creating a file <app_name>/android/app/src/main/kotlin/<app_uri>/Application.kt that registers the plugins, like:

package <your package>

import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
import com.tekartik.sqflite.SqflitePlugin
import com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin

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

    override fun registerWith(registry: PluginRegistry) {
        io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin.registerWith(
                registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"))

        com.tekartik.sqflite.SqflitePlugin.registerWith(
                registry.registrarFor("com.tekartik.sqflite.SqflitePlugin"))

        com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.registerWith(
                registry.registrarFor("com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin"))
    }
}
Share:
2,812
Lê Quang Bảo
Author by

Lê Quang Bảo

Updated on December 19, 2022

Comments

  • Lê Quang Bảo
    Lê Quang Bảo over 1 year

    I am building a notification system for my mobile application, the idea is to store both foreground and background messages received from Firebase Cloud message to SQLite. I've followed the guideline here to set up my project.

    Here my code:

    void initState() {
        super.initState();
        service = MySQLService();
    
        _firebaseMessaging.configure(
          onMessage: (Map<String, dynamic> message) async {
            print("onMessage: $message");
            service.insertItem(Item.fromMessage(message)); // It's working, here.
          },
          onBackgroundMessage: myBackgroundMessageHandler,
          onLaunch: (Map<String, dynamic> message) async {
            print("onLaunch: $message");
          },
          onResume: (Map<String, dynamic> message) async {
            print("onResume: $message");
          },
        );
    
        _firebaseMessaging.requestNotificationPermissions(
            const IosNotificationSettings(
                sound: true, badge: true, alert: true, provisional: true));
        _firebaseMessaging.onIosSettingsRegistered
            .listen((IosNotificationSettings settings) {
          print("Settings registered: $settings");
        });
        _firebaseMessaging.getToken().then((String token) {
          assert(token != null);
          print("Push Messaging token: $token");
        });
    }
    

    And the code for handling background message:

    Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) async {
      print("onBackground: $message");
      var service = MySQLService();
      await service.insertItem(Item.fromMessage(message)); // It's not working, here.
      return Future<void>.value();
    }
    

    For now, my application can: receive foreground message and store it into SQLite (by calling service.insertItem), But for some reason, background message is not stored in the SQLite even it displayed on the notification list of the device.

    enter image description here

    UPDATE: here the code for inserting message

      Future<void> insertItem(Item notification) async {
        final Database db = await MyDatabase.getDatabase();
        await db.insert('notification', notification.toMap(),
            conflictAlgorithm: ConflictAlgorithm.replace);
        return Future<void>.value();
      }
    
    • unbalanced_equation
      unbalanced_equation about 4 years
      Is the problem in iOS or Android or both? Could you share the code of MySQLService? Maybe you are not starting the service the right way? Take a look at this package, pub.dev/packages/sqflite , its suppose to do DB tasks in the background.
    • Lê Quang Bảo
      Lê Quang Bảo about 4 years
      I don't think there is anything wrong with my inserting function because it's working in case of foreground message. Only background message is not inserted. Maybe the onBackgroundMessageHangler didn't call from the start, can't say for sure because I don't know how to debug in the background.
    • unbalanced_equation
      unbalanced_equation about 4 years
      I think you can still somehow debug even if your app is closed. If your android phone for example is connected to your machine and a message is received in the background this will be executed print("onBackground: $message"); so check the log in your IDE. I think i have tried this once in Android IDE. If you notice your phone will still be logging so many things from different apps if its connected.
    • unbalanced_equation
      unbalanced_equation about 4 years
      When I asked if this doesn't work in Android or iOS due to the way you start the service, I had in mind the information in this page. flutter.dev/docs/cookbook/persistence/…. But like you said I guess if it worked in the foreground it must work in the background: the path is already correctly set.
    • Lê Quang Bảo
      Lê Quang Bảo about 4 years
      As far as I can tell print("onBackground: $message"); is not called. My emulator does receive the message, but nothing is logged on the debug console.
    • Renê Guilherme Nucci
      Renê Guilherme Nucci almost 4 years
      have you found a solution? The code in background seens not be trigered.. And i didnt find a way to debug precisily this.
    • Lê Quang Bảo
      Lê Quang Bảo almost 4 years
      I found some random comment on github stated that this is a bug and it hasn't fixed yet. So I decide to store information on the cloud (Firebase database) instead of SQLite.
    • b.john
      b.john almost 4 years
      I insert into mysql when the app s in the background but NOT when terminated. Help
    • Lê Quang Bảo
      Lê Quang Bảo almost 4 years
      I didn't found any solution for SQLite nor MySQL, if you are still in trouble then post your own question.
    • Mycodingproject
      Mycodingproject over 3 years
      I'm also having difficulty background messages to save in sqlite database using sqflite plugin. I searched so many topics about this problem but people is still having difficulties. onBackgroundMessage not firing when you have background messages.