Firebase: Update model from background message

Viewed 189

How would I implement a new state of my model when my app receives a message with a data object from our server while in the background? FirebaseMessaging.onBackgroundMessage() is working and I can see the data sent from the server but I can't figure out how to update the model with this new data. Assigning it to a variable works but reading this variable later returns null. I'm also using GetIt but can't call it when the app is in the background. All the tutorials just show how to receive the data (and just print it into console) but none show how to actually use it when the app is in the background.

Edit: I've checked multiple sources, which describe cloud messaging, for example https://firebase.flutter.dev/docs/messaging/usage/. Most of them just describe how to receive the message. Example:

Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
  print("Handling a background message: ${message.messageId}");
}

I've got this far. My message from the server contains a data object, which has the required model values (for example: "newBalance" : 100.00). But how do I update my model in my app with this new data while it is in the background? I don't seem to have access to it (as described above the assigned variable later returns null).

2 Answers

After almost a week of trying I think I found a workaround for my problem. Using the hive package the app writes the new data to the local database. However, calling Hive.initFlutter() was not working, while the app was in the background. So I wrote the path to the directory (await getApplicationDocumentsDirectory()).path into SharedPreferences in my main() method. Then, when the app receives a message while in the background, I call

if (Platform.isAndroid) {
  SharedPreferencesAndroid.registerWith();
} else if (Platform.isIOS) {
  SharedPreferencesIOS.registerWith();
}

(from packages shared_preferences_android and shared_preferences_ios) and afterwards I can retreive the path var dir = (await SharedPreferences.getInstance()).getString("keyForDir"); for Hive. Using Hive.init(dir) I'm able to open the box to write my data there (box.close() afterwards). Finally I can read the data from Hive when the app is in the foreground again (don't forget box.close() afterwards).

Phew, what a bad solution but it's the only one working for me. Some other people on the internet seem to have the same problem (https://github.com/flutter/flutter/issues/98473) and some other solutions worked for them but not for me.

Your options are limited, as the documentation says:

Since the handler runs in its own isolate outside your applications context, it is not possible to update application state or execute any UI impacting logic. You can, however, perform logic such as HTTP requests, perform IO operations (e.g. updating local storage), communicate with other plugins etc.

For example you can use Shared preferences plugin to save the received data from your background message handler like:

import 'package:shared_preferences/shared_preferences.dart';

Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
    final sharedPrefs = await SharedPreferences.getInstance();
    sharedPrefs.setString('data', <YOUR DATA FROM MESSAGE>);
}

Since your application is in the background at this time, you need to fetch this value after it is resumed. Check for a complete example here, but basically you need something like this in your main widget:

@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
  if (state == AppLifecycleState.resumed) {
    final sharedPrefs = await SharedPreferences.getInstance();
    final data = sharedPrefs.getString('data');
    print('Found in SharedPrefs: $data');
  }
}

After this you can find a way to update anything you need, already within your application's context.

Related