Flutterfire: [firebase_messaging]onLaunch is called multiple times

Created on 13 Oct 2019  ·  23Comments  ·  Source: FirebaseExtended/flutterfire

Hi,
My app uses firebase cloud messaging for push notifications. Unfortunately, onLaunch is called too often.
You’ll find an example app to reproduce the issue below (unfortunately, you have to follow all steps to configure the messaging plugin like state here (https://pub.dev/packages/firebase_messaging#-readme-tab-) before you can reproduce the issue).
Following scenario:
The app has two pages. If a push notification is received the app should open page 2. On Page two is a button to go back to page 1. After the user clicks on this button, the app is expected to be on page 1. This works fine, if the app wasn’t terminated when the notification was received. Alas, if it was terminated, the app opens page 1 just for a few minutes and jumps forward to page 2 again.

Steps to reproduce:
1) Start the app and note the
2) Terminate the app
3) Send a cloud push message to the noted token
4) --> A notification should appear
5) Click on the notification
6) --> Apps opens page 2
7) Click Button “Back to page 1”
8) --> < 9) --> <

Logs

Unfortunately, logs aren’t possible because is only happens, if the app was terminated previously…

C:\Users\x\Projekte\flutter_test\notification\flutter_app>flutter doctor -v
[√] Flutter (Channel beta, v1.5.4-hotfix.2, on Microsoft Windows [Version 10.0.17763.437], locale de-DE)
    • Flutter version 1.5.4-hotfix.2 at C:\Dev\flutter
    • Framework revision 7a4c33425d (2 weeks ago), 2019-04-29 11:05:24 -0700
    • Engine revision 52c7a1e849
    • Dart version 2.3.0 (build 2.3.0-dev.0.5 a1668566e5)


[√] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
    • Android SDK at C:\Users\x\AppData\Local\Android\sdk
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-28, build-tools 28.0.3
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1343-b01)
    • All Android licenses accepted.

[√] Android Studio (version 3.4)
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin version 35.3.1
    • Dart plugin version 183.6270
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1343-b01)

[√] Connected device (1 available)
    • Pixel 2 • xxxxx • android-arm64 • Android 9 (API 28)

Example App:

```main.dart

import 'package:flutter/material.dart';
import 'package:firebase_messaging/firebase_messaging.dart';

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

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;

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

class _MyHomePageState extends State {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();

@override
void initState() {
_firebaseMessaging.getToken().then((token) {
print(token);
});
_firebaseMessaging.configure(
onMessage: (Map message) => _redirectToPageTwo(),
onResume: (Map message) => _redirectToPageTwo(),
onLaunch: (Map message) => _redirectToPageTwo(),
);
super.initState();
}

_redirectToPageTwo(){
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => PageTwo()));
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text('Page1'),),
);
}
}

class PageTwo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Center(child: Text('Page2'),),
RaisedButton(child: Text('Go back to page 1'), onPressed: () => Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => MyHomePage())),)
],
),
);
}

}


```pubspec.yaml

name: flutter_app
description: A new Flutter application.

version: 1.0.0+1

environment:
  sdk: ">=2.1.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter


dev_dependencies:
  flutter_test:
    sdk: flutter
  firebase_core: ^0.4.0
  firebase_messaging: 5.0.0


flutter:

  uses-material-design: true

crowd messaging bug

Most helpful comment

Hello everyone ! Its a serious problem but i have done a work around that every notification has a unique id found in message['data']['google.message_id'] where message is the Map that u pass while configuring the fcm , if you can store that id and can perform operations.
Like if the id is same as the previous notification then just just do nothing and if its not then perform your operation u want to perform.

All 23 comments

@ehhc

The issue at https://github.com/flutter/flutter/issues/32698 has been closed and moved here. Future collaboration on this issue will be done here.

Any news from the devs?

I also have this problem. Is there any information available on when it will be fixed? And for now, is there any work around solution?

Thank you!

Hello,
This issue is always present even with the last version of the plugin.

I have been battling with this issue for some time now.

From main.dart my app uses routes: and onGenerateRoute: to navigate to the 12 other pages in the app.
From 'routes:' there are two pages that are navigated to using pushReplacementNamed and these pages navigate to the other pages from their side drawers using push and pop (simplistically). As others have said, sometime you just HAVE to use pushReplacementNamed.
I am using FCM to notify the user of several different events such as other people wanting to contact them. These notifications need to work when the app is active, in background, or closed. All good so far.
When the notification is received I use onMessage, onLaunch, or onResume to navigate to certain pages depending on the type of message received. I also show a dialogue, giving them a choice, in the case of onMessage and onResume.
The app also supports multiple languages so the text in the dialogues needs to be translated.
The app also uses scoped model to separate database logic (Firebase) from UI, some of the functions in scoped model are needed in the processing of the received notifications.

I would consider this a relatively complex, but normal, real-world app.

Because of the above and as far as my understanding of Flutter/Dart goes, the FCM handler (that has the call to .configure) needs to access context and scoped model in order to show the correctly translated dialogues and to navigate to the correct page. I cannot call .configure in main.dart initState because it doesn't have scoped model yet. Equally, it doesn't have a context that supports navigation or the data needed from the pages.

After many attempts at different 'solutions' and much Googling, StackOverflowing, Mediuming, etc. I settled on calling the FCM handler in the initStates of the two 'root' pages,. All notifications are then processed successfully whether the app is active, in background or closed. The only downside is the 'onLaunch' loop which shows itself whenever I switch between the two 'root' pages, FCM handler is fired again in the initState of each page and I get a repeat navigation of what was just shown in the other 'root' page. However, I HAVE to call the FCM handler at the opening of each of these pages in order to ensure the correct context for the text translation and dialogues. Hope that I am making sense. :-)

Anyway, I seem to have a simple workaround for now (hopefully, the behaviour gets fixed properly) so thought I would mention it here. I have set up two 'global' bools to ensure that onLaunch and onResume only fire once, irrespective of how many times I switch between the two 'root' pages. The bools are checked and set so:

onLaunch: (Map<String, dynamic> message) async {
  if (!globals.hasResumed && !globals.hasLaunched) {
    globals.hasLaunched = true;
<run dialogues from here depending on message type>      
...}
onResume: (Map<String, dynamic> message) async {
  if (!globals.hasResumed && !globals.hasLaunched) {
    globals.hasResumed = true;
<run dialogues from here depending on message type>      
...}

Hope this helps.

Hi @GrahamDi ,

thank you for your workaround solution. Unfortunately it doesn´t work for me, because the first click on a push works but the click on the second push notification doesn´t work. Or do I missunderstand something?

Thank you in advance.

@julian247 I would need to understand more about what you mean by 'first click on a push' and 'click on second push'. How are the notifications being sent? What is intended to happen when they are received? What is the state of the app when they are received, etc.? Maybe if you shared some code I could help.

Fyi, my FCM notifications are all sent from the Firebase backend using node.js cloud functions.

@GrahamDi Thank you for your fast reply.
I try to explain it in detail. The FCM notifications are sent by a PHP Script and a CURL call. This works well. The problem is that if the user clicks on the notification I want to push him to a special screen. When the user click on it, onLaunch fires multiple times. I have implemented that the onLaunch can only be called once. Now it works for the first click. But when an other notification arrives the onlaunch is not called because the Bool is already set to True from the other push notfication.
At my app, in every Page is at the bottom the Messaging_widget called with the configure... in it.

Do you understand the problem?

@julian247 I don't know much about PHP or curl but as you say the notifications are working fine on being sent. Some questions:

  1. To confirm, it is a Flutter app that the users are receiving the message with?
  2. If so, what is the 'first click' ie. what are they clicking on?
  3. Is the app opening from closed, in background, or are they receiving the message when it is active?
  4. Assuming that the app is closed, then I am puzzled why onLaunch then has the bool set to true. If it was closed, then when it restarts the bool should be reinitialised (I set it initially to false in the global config code file).
  5. When you say every page has the .configure call, do you navigate between these pages with push and pop or do you use pushReplacementNamed?

@GrahamDi

  1. Yes, it is.
  2. They click on the push notification they get on their phone. The app is in the background at this moment.
  3. The problem occurs when the app is in the background.
  4. When the app is closed, it works fine.
  5. I navigate between the pages mostly with push.

Thank you! :)

@julian247 I find my simple fix is not working perfectly, so I guess it is not a fix :-(
I am going to spend more time on it today.
One thing I would suggest for your situation is only to call .configure once, in the initstate of the root (first) page in your app. As long as that root page is never closed (it isn't when you 'push' to another page and then 'pop' back), FCM should work fine. It does in my case. I only get the problem when I have to pushreplace the root page with an alternate one, which calls another FCM .configure ie. the problem I experience is when configure is called more than once. The problem appears to be that the 'old' configure is not overridden and the two calls cause conflict.

@GrahamDi Thank you for the input. Now only the main screen calls it. But now I have the problem, that when i click on the main screen it navigates me automatically to the other screen. I think you solved this problem with the bools. Can you please describe this in more detail?

Thank you!

@julian247
That almost sounds like the onLaunch loop is still there. Are you absolutely sure that FCM.configure is only being called once? If you haven't already, perhaps put print statements at appropriate places in the .configure method to double check. My bools were simply my (bad) attempt to handle the multiple calling of .configure.
I now appear to have solved my problem by replacing my Navigator.pushReplacementNamed calls with Navigator.pushNamed and so making sure that all my pages are children of my home page. I then call FCM.configure only once in the home page initState. I have removed the bools as I no longer need them. Everything seems to be working fine, although I am still doing testing.
Also, do a flutter clean and a complete rebuild of the app, if you haven't already of course :-)

@GrahamDi Yes, unfortunately it is. :-/
And yes, i have removed all the other calls.
I try to Test the the pushNamed idea. Thank you:)

@julian247

I fear I am out of ideas then, without seeing your code. :-(

Sorry and all that.

Just to say that pushReplacementNamed closes the page being replaced, so if the page you call configure from is that page then maybe that also impacts FCM.configure or it is called again.
My code seems to work now that I never close the page that called FCM.configure. Good luck.

I have a similar issue, what would help me it is a clear function for FCM configuration. For now I'm just using a simple validation to avoid my problem.

@kroikie This issue still appears in the latest plugin. Please take a look. This bug only occurs in Android. I assume it's because the app is not completely terminated when you leave an app by pressing the back button.

This is occurring for me on iOS too

Hello everyone ! Its a serious problem but i have done a work around that every notification has a unique id found in message['data']['google.message_id'] where message is the Map that u pass while configuring the fcm , if you can store that id and can perform operations.
Like if the id is same as the previous notification then just just do nothing and if its not then perform your operation u want to perform.

@Soumyadeep21 Thanks, it works!

News on this?

Hey all 👋

As part of our roadmap (#2582) we've just shipped a complete rework of the firebase_messaging plugin that aims to solve this and many other issues.

If you can, please try out the dev release (see the migration guide for upgrading and for changes) and if you have any feedback then join in the discussion here.

Given the scope of the rework I'm going to go ahead and close this issue in favor of trying out the latest plugin.

Thanks everyone.

Was this page helpful?
0 / 5 - 0 ratings