简体   繁体   中英

Flutter - FCM: how to store background message to SQLite

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.

在此处输入图像描述

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();
  }

For some reason onBackgroundMessage is only called when the FCM message does not contain a notification (eg 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"))
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM