Reputation: 6458
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.
Upvotes: 1
Views: 3464
Reputation: 6458
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));
}
});
}
Upvotes: 2