[Firebase Messaging]: Background Message Handler method not called when the app in background?
Asked Answered
T

7

20

I-am developing an app for pushing notifications using FCM, when the app in background or terminated and receiving new notification i need to save the data of this notification local in (SQLITE), without click on the notification or re-open the app again, the notification not read in the app unless clicked. Any suggestions?

This my NotificationHandler.dart

import 'dart:async';
import 'dart:io';
import 'package:eshaar/model/message.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:intl/intl.dart';
import 'package:eshaar/model/database_helper.dart';


FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
new FlutterLocalNotificationsPlugin();

Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) async {
  print("onBackgroundMessage: $message");
  //_showBigPictureNotification(message);
  return Future<void>.value();
}


Future onSelectNotification(String payload) async {

}

class NotificationHandler {
  FirebaseMessaging _fcm = FirebaseMessaging();
  StreamSubscription iosSubscription;
  DatabaseHelper db = new DatabaseHelper();
  static final NotificationHandler _singleton =
  new NotificationHandler._internal();

  factory NotificationHandler() {
    return _singleton;
  }
  NotificationHandler._internal();

  initializeFcmNotification() async {
    var initializationSettingsAndroid =
    new AndroidInitializationSettings('mipmap/ic_launcher');
    var initializationSettingsIOS = new IOSInitializationSettings(
        onDidReceiveLocalNotification: onDidReceiveLocalNotification);
    var initializationSettings = new InitializationSettings(
        initializationSettingsAndroid, initializationSettingsIOS);
    flutterLocalNotificationsPlugin.initialize(initializationSettings,
        onSelectNotification: onSelectNotification);

    if (Platform.isIOS) {
      iosSubscription = _fcm.onIosSettingsRegistered.listen((data) {
        // save the token  OR subscribe to a topic here
      });

      _fcm.requestNotificationPermissions(IosNotificationSettings());
    } else {
      _saveDeviceToken();
    }

    _fcm.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("---------------------On Message--------------------");
        displayNotification(message['data']['title'],message['data']['body']);
        _saveNotificationToLocal(message);
      },
      onBackgroundMessage: Platform.isIOS ? null : myBackgroundMessageHandler ,
      onLaunch: (Map<String, dynamic> message) async {
        print("---------------------On Lunch--------------------");
        _saveNotificationToLocal(message);
      },
      onResume: (Map<String, dynamic> message) async {
        print("---------------------On Resume--------------------");
        _saveNotificationToLocal(message);
      },
    );
  }

  _saveNotificationToLocal(Map<String, dynamic> message){
    String sender_id = message['data']['sender_id'];
    String message_id = message['data']['message_id'];
    String title = message['data']['title'];
    String body = message['data']['body'];
    String del_time  = getCurrentDateTime();
    Messages msg = new Messages(sender_id,message_id,title,body,del_time);
    db.saveMessages(msg);

  }

 String getCurrentDateTime(){
   var now = new DateTime.now();
   var formatter = new DateFormat('MMM d yy h:mm a');
   String formatted = formatter.format(now);
   return formatted;
  }

  /// Get the token, save it to the database for current user
  _saveDeviceToken() async {
    String fcmToken = await _fcm.getToken();
  }


  Future<void> onDidReceiveLocalNotification(
      int id, String title, String body, String payload) async {
    // display a dialog with the notification details, tap ok to go to another page
  }

  void displayNotification(String title,String body) async{
    var androidPlatformChannelSpecifics = AndroidNotificationDetails(
        'your channel id', 'your channel name', 'your channel description',
        importance: Importance.Max, priority: Priority.High, ticker: 'ticker');
    var iOSPlatformChannelSpecifics = IOSNotificationDetails();
    var platformChannelSpecifics = NotificationDetails(
        androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
    await flutterLocalNotificationsPlugin.show(
        0, '$title', '$body', platformChannelSpecifics,
        payload: 'item x');
  }
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="ot.eshaar.eshaar">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    <uses-permission android:name="android.permission.VIBRATE" />



    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
         calls FlutterMain.startInitialization(this); in its onCreate method.
         In most cases you can leave this as-is, but you if you want to provide
         additional functionality it is fine to subclass or reimplement
         FlutterApplication and put your custom class here. -->
    <application
        android:name=".Application"
        android:label="eshaar"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- This keeps the window background of the activity showing
                 until Flutter renders its first frame. It can be removed if
                 there is no splash screen (such as the default splash screen
                 defined in @style/LaunchTheme). -->
            <meta-data
                android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
                android:value="true" />


            <meta-data
                    android:name="com.google.firebase.messaging.android.channel_id"
                    android:value="mmne" />

            <meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="@string/default_notification_channel_id"/>


            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>

            <intent-filter>
                <action android:name="FLUTTER_NOTIFICATION_CLICK" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"></action>
            </intent-filter>
        </receiver>
        <receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />


    </application>
</manifest>

Flutter doctor -v

[✓] Flutter (Channel stable, v1.9.1+hotfix.6, on Mac OS X 10.14.4 18E226, locale
    en-US)
    • Flutter version 1.9.1+hotfix.6 at /Users/mahmoudabdelaziz/Desktop/flutter
    • Framework revision 68587a0916 (3 months ago), 2019-09-13 19:46:58 -0700
    • Engine revision b863200c37
    • Dart version 2.5.0


[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
    • Android SDK at /Users/mahmoudabdelaziz/Library/Android/sdk
    • Android NDK location not configured (optional; useful for native profiling
      support)
    • Platform android-28, build-tools 28.0.3
    • Java binary at: /Applications/Android
      Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build
      1.8.0_152-release-1343-b01)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 11.0)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 11.0, Build version 11A420a
    • CocoaPods version 1.8.4

[!] Android Studio (version 3.4)
    • Android Studio at /Applications/Android Studio.app/Contents
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
    • Java version OpenJDK Runtime Environment (build
      1.8.0_152-release-1343-b01)

[✓] IntelliJ IDEA Community Edition (version 2018.3.3)
    • IntelliJ at /Applications/IntelliJ IDEA CE.app
    • Flutter plugin version 31.3.4
    • Dart plugin version 183.5153.38

[✓] Connected device (1 available)
    • Android SDK built for x86 • emulator-5554 • android-x86 • Android 9 (API
      28) (emulator)
Threeply answered 12/12, 2019 at 6:27 Comment(0)
S
19

In my case, FCM onBackgroundMessage is triggered when only there is no notification on the payload. if there a notification on the payload what it does is it sends shows you notification on the system tray.

But if you want to do some compilation on the data sent then remove the notification on the payload and send data only.

{
  "to" : "Place the token of the test device here",
  "click_action": "FLUTTER_NOTIFICATION_CLICK",
  "priority": "high",
  "data" : {
     "type" : "anything",
     "title": "Title of Your Notification in Title",
     "key_1" : "Value for key_2"
  }

}

UPDATED 2021

Make sure to add content_available on the notification this worked for me now. I am using firebase_messaging prereplase. On this release they have enabled background handler out of the box.
https://pub.dev/packages/firebase_messaging/versions/8.0.0-dev.14

{  
  "data":{  
    "title":"mytitle",
    "body":"mybody",
    "url":"myurl"
  },
 "notification":{  
    "title":"mytitle",
    "body":"mybody",
    "content_available": true
  },
  "to":"/topics/topic"
  "click_action": "FLUTTER_NOTIFICATION_CLICK",
}
Superego answered 6/5, 2020 at 22:13 Comment(3)
this really works. BUT fcm dont support data when app is completly terminated.Insurance
In your scenario, you need both notification and data. Notification is processed by the onBackgroundMessage handler and data is processed by the onMessage. Priority high is also important as specified.Filaria
I was sending the notification from a Java server. In my case, removing the notification from the message resolved the issue. ThanksAlmire
B
13

It's late but still I'll respond for others to be helpful.

The issue here isn't with the handling but with type of message you are sending from the Firebase console. onBackgroundMessage callBack is called only if the message is a data message or a notification message with a data payload.

And make sure your message also have click_action: 'FLUTTER_NOTIFICATION_CLICK' added.

And there as well is some issue with generating messages from Firebase console, so try to generate messages directly from your server. For more details please checkout this thread.


Berrie answered 16/4, 2020 at 10:53 Comment(5)
I tried send a notification via firebase console but not a test message, a fully message with key value data, but not working too... Seens that not trigering my background handlerBronson
Try sending a message to a specific device using device fcm token.Berrie
Yep, thanks, I got it when send a notification with data only,Bronson
Great it solved ur problem and don't forget to upvote the answer, so that others can find the solution easily.Berrie
It's worked. click-action was missed. Thank you lotSolvency
L
5

please make sure that you have added click_action property while sending the notification. make sure its value matches with the one you have provided in the android intent.

{
notification: {
    title: 'Title',
    body: 'Body',
    click_action: 'FLUTTER_NOTIFICATION_CLICK'
} }
Laritalariviere answered 12/12, 2019 at 13:37 Comment(0)
M
0

In myBackgroundMessageHandler you need to call that function to save the notification to your local storage.

For example

Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) async {
  print("onBackgroundMessage: $message");
  //_showBigPictureNotification(message);
    NotificationHandler()._saveNotificationToLocal(message);
  return Future<void>.value();
}

Hope that helps.

Melisenda answered 12/12, 2019 at 10:53 Comment(4)
Thanks for your response, but it still not working while app in background and not save the data unless clicking on the notification. Also, the function does not print the sentence.Threeply
This is how i have mine structured. Future<dynamic> _myBackgroundMessageHandler( Map<String, dynamic> message) async { if (message.containsKey('data')) { // Handle data message final dynamic data = message['data']; print(data); myAppState().processFCMessageEvent(data); } }Melisenda
Must also make sure you arent sending a notification if you intend on using backgroundmessageMelisenda
This helped me out a lot have a look at this link github.com/FirebaseExtended/flutterfire/issues/199 and look for the comment written by mobileinkMelisenda
M
0

With postman send this and "content_available": true trigger _firebaseMessagingBackgroundHandler

{
    "to": "_Token",
   "notification": {
        "title": "Title",
        "body": "body body body"
    },"data": {
      "title": "Title",
      "message": "body body body"

   },
   "apns": {
       "headers": {
           "apns-priority": "5"
       }
   },
   "priority": "high",
      "content_available": true
}
Mcconnell answered 13/10, 2022 at 8:51 Comment(0)
W
0

add this line at the top of your background handler message. @pragma('vm:entry-point')

Wean answered 23/2, 2023 at 3:29 Comment(1)
This is needed only for the tree shake not to ignore the background handler method when the app is built for the release mode.Berman
O
0

Use this format

{
  "data": {
    "message": "YOUR MSG",
    "title": "TITLE"
  },
  "content_available": true,
  "priority": "high",
  "to": "DEVICE TOKEN"
}
Oxalis answered 24/3, 2023 at 4:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.