onMessage: callback called twice on incoming messages Flutter web

2,079

As it turns out..a good flutter clean, Android Studio and machine restart solved it..

Now I'm not receiving the same message twice, but for those who actually are experimenting duplicate messages this will solve it, dough it's just a workaround.

Just declared int lastMessageId = 0 variable. When a message comes in, check its id against it and, only if they're different, save the new message id as lastMessageId and display the message.

Stream<PushNotificationState> _mapListenToMessagesToState(ListenToMessages event) async* {
    print('_mapListenToMessagesToState started');
    int lastMessageId = 0;

    _pushNotificationSwitcher.onMessage().listen((message) {
      print('incoming message is : $message');
      var data = message['data'];
      int messageId = int.parse(data['id']);

      if( lastMessageId != messageId) {
        lastMessageId = messageId;
        print(
            '_mapListenToMessagesToState onMessage() listener: received new message');
        add(ReceivedNewMessage(message: message));
      }
    });
  }
Share:
2,079
Vincenzo
Author by

Vincenzo

At age of 40 an accident forced me in bed for a few months and I realised that I wanted point my life in a different direction to really fulfil my purpose in life, help others and help making this planet a better place. I love to create stuff and I constantly have perhaps too many ideas. I'm now launching a bicycle related start-up. I got back into coding after 25 years away from it as it was a fundamental skill to get updated to get the project started and since then I'm working on the start-up's products. I first learned Swift as it seemed the easiest language I could learn at fast pace, but when Apple created SwiftUI framework ( Flutter copycat A.F.A.I.K ) I decided to move away from Swift the obvious choice has been learning Flutter ( the original ). So happy I made the transition and I'm not looking back.

Updated on December 24, 2022

Comments

  • Vincenzo
    Vincenzo over 1 year

    In my app, for the web version, I use package firebase 7.3.0 for Firebase services and I'm now setting up FCM for web too. When I get a new message with the app in foreground the onMessage: from messaging() gets triggered twice. It also used to happen for the flutter_messaging device plugin prior to a certain Flutter version but is now solved.

    I basically set a StreamTransformer to get the message in the same type of Map<String, dynamic> as the flutter_messaging device package used in PlatformPushNotificationDevice, I use a Stub to switch classes depending on the platform. In the web class PlatformPushNotificationWeb I instantiate messaging as var firebaseMessaging = messaging(); and declare my methods, one of which is onMessage() :

    Stream<Map<String, dynamic>> onMessage()  async* {
    
        print('PlatformPushNotificationWeb.onMessage() started');
        handleData(Payload payload, EventSink<Map<String, dynamic>> sink) {
            Map<String,dynamic> message = {
              'notification': {
                'title': payload.notification.title,
                'body': payload.notification.body,
                'sound': true
              },
              'data': payload.data
            };
          sink.add(message);
        }
    
        final transformer = StreamTransformer<Payload, Map<String, dynamic>>.fromHandlers(
            handleData: handleData);
    
        yield* firebaseMessaging.onMessage.transform(transformer);
      }
    

    so the listener in my bloc will receive the same message type no matter the platform,

    Stream<PushNotificationState> _mapListenToMessagesToState(ListenToMessages event) async* {
        print('_mapListenToMessagesToState started');
        _pushNotificationSwitcher.onMessage().listen((message) {
          print('_mapListenToMessagesToState onMessage() listener: received new message');
          add(ReceivedNewMessage(message: message));
        });
      }
    

    but for every incoming message I get the listener responding twice.

    I set the index.html as:

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <title>fixit cloud biking</title>
      <!--  <meta name="google-signin-client_id" content="YOUR_GOOGLE_SIGN_IN_OAUTH_CLIENT_ID.apps.googleusercontent.com">-->
      <meta name="google-signin-client_id" content="xxxx.apps.googleusercontent.com">
    <!--  <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">-->
    </head>
    <!--<body>-->
    <body id="app-container">
    <script src="main.dart.js?version=45" type="application/javascript"></script>
    <!-- The core Firebase JS SDK is always required and must be listed first -->
    <script src="https://www.gstatic.com/firebasejs/7.19.1/firebase-app.js"></script>
    
    <!-- TODO: Add SDKs for Firebase products that you want to use
         https://firebase.google.com/docs/web/setup#available-libraries -->
    <script src="https://www.gstatic.com/firebasejs/7.19.1/firebase-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.19.1/firebase-analytics.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.19.1/firebase-messaging.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.19.1/firebase-storage.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.19.1/firebase-database.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.19.1/firebase-remote-config.js"></script>
    
    
    <script>
    if ("serviceWorker" in navigator) {
      window.addEventListener("load", function () {
         //navigator.serviceWorker.register("/flutter_service_worker.js");
        navigator.serviceWorker.register("/firebase-messaging-sw.js");
      });
    }
    </body>
    </html>
    

    and the firebase-messaging-sw.js file as :

    importScripts("https://www.gstatic.com/firebasejs/7.19.1/firebase-app.js");
    importScripts("https://www.gstatic.com/firebasejs/7.19.1/firebase-messaging.js");
    
    firebase.initializeApp({
      apiKey: "xxxx",
                authDomain: "xxxx",
                databaseURL: "xxxx",
                projectId: "xxx",
                storageBucket: "xxxx",
                messagingSenderId: "xxxx",
                appId: "xxx",
                measurementId: "G-xxxx",
    });
    const messaging = firebase.messaging();
    messaging.setBackgroundMessageHandler(function (payload) {
    console.log('[firebase-messaging-sw.js] Received background message ', payload);
    
        const promiseChain = clients
            .matchAll({
                type: "window",
                includeUncontrolled: true
            })
            .then(windowClients => {
                for (let i = 0; i < windowClients.length; i++) {
                    const windowClient = windowClients[i];
                    windowClient.postMessage(payload);
                }
            })
            .then(() => {
                return registration.showNotification("New Message");
            });
        return promiseChain;
    });
    self.addEventListener('notificationclick', function (event) {
        console.log('notification received: ', event)
    });
    
    

    Could it be that instantiating messaging in the PlatformPushNotificationWeb class and in the firebase-messaging-sw.js file the reason of onMessage: callback being triggered twice? Many thanks.