Flutter Firebase Cloud Messaging onMessage is triggered twice

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.

Upvotes: 14

Views: 11508

Answers (6)

Shubh Damodar
Shubh Damodar

Reputation: 51

I faced this issue with firebase_messaging: 13.1.0 & firebase_core: 1.24.0


Temporary Solution,


String newMessageId = '';
FirebaseMessaging.onMessage.listen((RemoteMessage? message) {
  if (message!.messageId != newMessageId) showSimpleNotification(message);
  newMessageId = message.messageId!;
});`

Upvotes: 4

rasulfahad
rasulfahad

Reputation: 622

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.

Upvotes: 4

werainkhatri
werainkhatri

Reputation: 109

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.

Upvotes: 9

Alphapico
Alphapico

Reputation: 3041

Update to latest FireFlutter package solve this issue:

For non-null safety:

firebase_messaging: ^8.0.0-dev.15
firebase_core: ^0.7.0

Upvotes: 0

Sludge
Sludge

Reputation: 7462

I had this same issue and what caused it was that I had recently renamed my project. I noticed (after too long) that one of the notification banners said the old app name and one said the new app name. After uninstalling and then reinstalling the app (along with flutter clean), the double notifications went away.

In short, if nothing else works, try uninstalling and reinstalling your app again.

Upvotes: 0

Ratna Priya
Ratna Priya

Reputation: 1

This is the perfect code for this issue. Just we need to do is to declare a static bool variable, It will work with onResume(). Before only some logic is missing, that's why it is calling twice. Now, It is working for me. Hope, you will also get help from this.

onMessage: // ignore: missing_return (Map<String, dynamic> msg) { if(!isNotified) { print("onMessage called"); print(msg); DocumentReference documentReference = Firestore.instance.collection('PushNotifications').document(); documentReference.setData({ "payload": msg }); } isNotified = !isNotified; }

Image of Console Output

Github Link

Upvotes: 0

Related Questions