I already implemented the basic configuration suggested by the firebase_messaging flutter package. However, each time that I receive a notification on my flutter app onMessage is triggered twice. I'm using firebase_messaging 6.0.9, Dart 2.7.0 and Flutter 1.12.13+hotfix.5.
This is my [project]/android/build.gradle
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.google.gms:google-services:4.3.2'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}
This is my [project]/android/app/build.gradle
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 28
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.chat_notification"
minSdkVersion 16
targetSdkVersion 28
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}
apply plugin: 'com.google.gms.google-services'
And this is the code where the onMessage is triggered twice
import 'package:chat_notification/model/message.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
class MessageWidget extends StatefulWidget {
@override
_MessageWidgetState createState() => _MessageWidgetState();
}
class _MessageWidgetState extends State<MessageWidget> {
FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
List<Message> messages = [];
@override
void initState() {
// TODO: implement initState
super.initState();
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> response) async {
print("onMessage: $response");
},
onLaunch: (Map<String, dynamic> response) async {
print("onLaunch: $response");
},
onResume: (Map<String, dynamic> response) async {
print("onResume: $response");
},
);
}
@override
Widget build(BuildContext context) => ListView(
children: messages.map(buildMessage).toList(),
);
Widget buildMessage(Message message) => ListTile(
title: Text(message.title),
subtitle: Text(message.body),
);
}
I already try creating new projects but it seems to happen for each of them. I would really appreciate if anyone can help me about this.
EDIT:
This is no longer present in latest version of firebase_messaging: 7.0.0. Best solution I found is updating the package. Duplicated messages are not longer present.
FirebaseMessaging _firebaseMessaging = new FirebaseMessaging(); _firebaseMessaging. configure( onMessage: (Map<String, dynamic> message) { }, onResume: (Map<String, dynamic> message) { }, onLaunch: (Map<String, dynamic> message) { }, ); _firebaseMessaging. getToken(). then((token) { saveToken(token); });
With FCM, developers can send two types of messages to users: notifications messages and data messages. Notification messages are displayed on the user's device by FCM on behalf of the application. Data messages are directly processed by the application, which is responsible for delivering the message to the user.
What does it do? Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that lets you reliably send messages at no cost. Using FCM, you can notify a client app that new email or other data is available to sync. You can send notification messages to drive user re-engagement and retention.
To start using the Cloud Messaging package within your project, import it at the top of your project files: Before using Firebase Cloud Messaging, you must first have ensured you have initialized FlutterFire. To create a new Messaging instance, call the instance getter on FirebaseMessaging:
FlutterFirebase Messaging does now support debugging and hot reloading for background isolates, but only if your main isolate is also being debugged, (e.g. run your application in debug and then background it by switching apps so it's no longer in the foreground).
After fast investigation, the method onAttachedToEngine of the class FirebaseMessagingPlugin, which call registerReceiver, is called twice. That explain the double call.
Update:
This is no longer present in latest version of firebase_messaging (7.0.0 or up). Best solution I found is updating the package. Duplicated messages are not longer present.
Original Solution:
I am facing the same problem. I don't know the reason or the solution for it. A simple way-around is using a even odd counter to catch only one of the two callbacks.
import 'package:chat_notification/model/message.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
class MessageWidget extends StatefulWidget {
@override
_MessageWidgetState createState() => _MessageWidgetState();
}
class _MessageWidgetState extends State<MessageWidget> {
FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
List<Message> messages = [];
static int i = 0;
@override
void initState() {
// TODO: implement initState
super.initState();
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> response) async {
if(i%2==0) {
print("onMessage: $response");
// something else you wanna execute
};
i++;
},
onLaunch: (Map<String, dynamic> response) async {
print("onLaunch: $response");
},
onResume: (Map<String, dynamic> response) async {
print("onResume: $response");
},
);
}
@override
Widget build(BuildContext context) => ListView(
children: messages.map(buildMessage).toList(),
);
Widget buildMessage(Message message) => ListTile(
title: Text(message.title),
subtitle: Text(message.body),
);
}
This will run the code only once! Can be used for onLaunch and onResume similarly.
I faced this issue with firebase_messaging: ^10.0.4
Going through literature, I see that this is a bug that keeps occurring every now and then across versions.
Knowing that, I personally wouldn't use the methods mentioned in the answer (using a boolean flag, odd/even flag).
I got around the issue by using a time controlled semaphore.
static int semaphore = 0;
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
if (semaphore != 0) {
return;
}
semaphore = 1;
Future.delayed(Duration).then((_) => semaphore = 0);
handleMessage(message);
});
Also avoids concurrent remote message launches (based on the Duration you pass) - though I can't imagine a real world situation where remote messages might be launched concurrently.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With