Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[firebase_messaging] background messaging documentation prob #199

Closed
mobileink opened this issue Sep 22, 2019 · 45 comments · Fixed by #252
Closed

[firebase_messaging] background messaging documentation prob #199

mobileink opened this issue Sep 22, 2019 · 45 comments · Fixed by #252
Labels
impact: crowd Affects many people, though not necessarily a specific customer with an assigned label. (P2) type: documentation Improvements or additions to documentation

Comments

@mobileink
Copy link

Describe the bug
It took me about three hours to figure out why my background message handler wasn't working. The documentation says "Define a top level Dart method to handle background messages." This is confusing, since there are no top level "methods", only top-level functions. Naturally I defined a handler in my class (it works for onMessage, ought to work for onBackgroundMessage, right?) and had to dig through the flutter source to figure out the problem.

To Reproduce
Steps to reproduce the behavior:

  1. Define your onBackgroundMessage callback the same way you define your onMessage handler, as a method in the class that calls _firebaseMessaging.configure
  2. Result:
    The following NoSuchMethodError was thrown building Builder:
    I/flutter (25669): The method 'toRawHandle' was called on null.
    I/flutter (25669): Receiver: null
    I/flutter (25669): Tried calling: toRawHandle()

Expected behavior

The documentation should be amended to indicate that the handler must be either a top level function, or a static method.

Thanks for the plugin!

@mobileink mobileink added the type: bug Something isn't working label Sep 22, 2019
@XExistence
Copy link

Can you be more specific on how exactly you solved the problem. Tried making the handler static and it broke during compile time

@kleinpetr
Copy link

I have the same problem. Can you update the documentation? I am confused by this

  1. Add an Application.java class to your app (where??)
  2. Set name property of application in AndroidManifest.xml (where??, how??)
  3. Define a top level Dart method to handle background messages (where?? at the same time when I configured onMessage callback?)

Thank you for help.

@serendipity1004
Copy link

Yes I have the same question as @kleinpetr

@mobileink
Copy link
Author

mobileink commented Sep 24, 2019 via email

@kleinpetr
Copy link

kleinpetr commented Sep 25, 2019

I can't configure firebase at the main.dart because I have some logic about this. For example I am initing the FCM only if user is logged, etc. So I have to configure firebase inside a class. You said the background callback must be static, OK I set static method, but still not working.

main.dart
initState(){
  ....
  HelperClass.initFCM(some dependencies);
}

helperClass.dart
class HelperClass
{
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();

  static Future<dynamic> backgroundMessageHandler(Map<String, dynamic> message) {
  print("_backgroundMessageHandler");
  if (message.containsKey('data')) {
    // Handle data message
    final dynamic data = message['data'];
    print("_backgroundMessageHandler data: ${data}");
  }

  if (message.containsKey('notification')) {
    // Handle notification message
    final dynamic notification = message['notification'];
    print("_backgroundMessageHandler notification: ${notification}");
  }

  Future<void> initFCM(...){
    _firebaseMessaging.configure(
      .....,
      onBackgroundMessage: backgroundMessageHandler,
      .....
      );
  }
}

@XExistence
Copy link

XExistence commented Sep 25, 2019

Okay I don't know how I managed to get mine to work honestly. I just followed all the instructions and commented out the onBackgroundMessage because it was giving fatal compile time errors.

firebaseMessaging.configure(
//onBackgroundMessage: _backgroundMessageHandler,

  onMessage: _messageHandler,

  onLaunch: _lauchHandler,

  onResume: _resumeHandler,
);

like that but I'm confused why my _backgroundMessageHander top-level function gets triggered even on app terminated or forground.

I also made sure that all my handlers including message, resume, and launch are top-level functions. (NOT IN ANY CLASS). I hope this helps someone because I've been struggling with this for days.

@mobileink
Copy link
Author

mobileink commented Sep 25, 2019 via email

@kleinpetr
Copy link

kleinpetr commented Sep 26, 2019

What do you mean top-level handler? I need working with variables and methods from the class in fcm calbacks.. is there some chance ro declare onBackgroundMessage callback the same way as other callbacks??

@luckycreationsindia
Copy link

Please update example of firebase_messaging where it handles background messages. Seriously it's very confusing or anyone please upload demo working project for handling onBackground messages.

@ektelat
Copy link

ektelat commented Sep 28, 2019

image

we can't understand this point

@kleinpetr
Copy link

@ektelat It means you need to use your custom Application class instead of the default flutter Application class

You have this in manifets (probably)
<application android:name="io.flutter.app.FlutterApplication" android:label="Name app" android:icon="@mipmap/ic_launcher">

And you need replace io.flutter.app.FlutterApplication to your Application class for example com.yourdomain.Application or or .Application (maybe)

@ektelat
Copy link

ektelat commented Sep 29, 2019

@kleinpetr
i changed it as you told me but the app can't be installed all the time it crashed

image

image

stop here....

@kleinpetr
Copy link

@ektelat show me your Application.java please

@ektelat
Copy link

ektelat commented Sep 29, 2019

@kleinpetr i get my mistake, i just copy the same example of flutter document so it's with
package io.flutter.plugins.firebasemessagingexample;
i changed it and it's installing now put after it's loaded unfortinatlly it crash again.
after i removed onBackgroundMessage from messaging configure

image
it's work now.

so what to do with: onBackgroundMessage i need it

here the myBackgroundMessageHandler:

image

what is my problem??

@kleinpetr
Copy link

@ektelat Yes I stoped in the same moment as you... I don't know where is the problem. I still waiting for update example on pub.dev ... or some answer here..

@nhwilly
Copy link

nhwilly commented Sep 29, 2019

I've had so much trouble making any of this work, I don't dare try background stuff. So I have not attempted to use background notifications.

I have noticed, however, that none of my messages, sent either by the firebase messaging console or the firebase Admin SDK ever reach the system tray. Doesn't matter that the app is terminated, or background or whatever. Gmail notifications are still showing up, by mine don't. Notifications enabled and double checked for my app.

If the app is running, I get the messages onMessage just fine. Interestingly, if the app is terminated, when it is launched, I get an the same onMessage. Probably because it never went to the system tray where it would wait for me to tap it. No tapping would mean no onResume or onLaunch right?

I finally created an app that just tries to do push, nothing else. Same thing.

Working great on Android.

I could do without the background stuff if my messages would hit the system tray.

@luckycreationsindia
Copy link

Ok I got it worked.

If u send any notification from firebase console it'll never call onBackground...

Try postman or any other to send notification using fcm notification api. And send only data message. Remove title and body from notification parameter... Now you will get onBackground.

@nhwilly
Copy link

nhwilly commented Sep 30, 2019

That's makes sense, right? Messages with notifications go to the system tray, so only "data only" messages will trigger background logic. And the console forces you to send title and body, so it's a notification message.

I just can't get a simple notification to show in the system tray. Wrong thread I guess.

Good to hear you got it working.

@mobileink
Copy link
Author

mobileink commented Oct 2, 2019 via email

@nhwilly
Copy link

nhwilly commented Oct 2, 2019

@mobileink You're 100% on this one.

When I was working in Xamarin, you actually had to have a second app, especially in iOS, that knew what to do with the incoming data only messages.

My solution was to create a local notification and dump it into the system tray myself so it would show up - but only if I needed it to. At that point, all my messages were data messages, but I was adding content to them so I could show them if needed.

That approach started with a "logout all device" where I wanted to clear access tokens on every device the user had and ended up adding buttons so actions could be taken directly in the notification in the system tray.

It was pretty cool, but Xamarin Forms finally did me in after a couple of years and I gave it up.

@nerder
Copy link
Contributor

nerder commented Oct 2, 2019

Sorry for the maybe dumb question, but how can i send a notification that triggers such callback?

@kleinpetr
Copy link

@nerder for example like this https://medium.com/android-school/test-fcm-notification-with-postman-f91ba08aacc3

@aleaforny
Copy link

On Mon, Sep 23, 2019 at 12:05 PM kleinpetr @.***> wrote:
Here's what worked for me (with names changed to protect the innocent): In [...]

Thank you very much, sir, I guess you have the solution. The official wiki is incomplete, in my opinion.
If you don't mind, can you tell us what values you use for these two dependencies in android\build.gradle (these values below are mine):
classpath 'com.android.tools.build:gradle:3.3.0'
classpath 'com.google.gms:google-services:4.3.0'

In addition to this issue, I've got a very strange behavior :
I defined myBackgroundMessageHandler in my class which has _firebaseMessaging.configure (I've defined it as static, of course)
But, I did NOT add onBackgroundMessage: _backgroundMessageHandler handler to _firebaseMessaging.configure.... Guess what? My background messages are well handled and working as expected !!! (at least, for those when my app is running in the background - I did not test when the app is terminated, as I'm not able to see the print out on Flutter console to confirm if it's working as well or not)

Is this an expected behavior? Or am I missing something in my understanding of this plugin (which is totally possible as well, I'm not very familiar with flutter FCM)

Thanks y'all !

@iapicca iapicca changed the title [<firebase_messaging>] <background messaging documentation prob> [firebase_messaging] background messaging documentation prob Oct 24, 2019
@iapicca
Copy link

iapicca commented Oct 24, 2019

I'm leaving this issue open
as a reference for the documentation,
please follow up on the link below for the bug,
thank you

#88

@iapicca iapicca added type: documentation Improvements or additions to documentation and removed type: bug Something isn't working labels Oct 24, 2019
@iapicca iapicca added the impact: crowd Affects many people, though not necessarily a specific customer with an assigned label. (P2) label Oct 24, 2019
@mobileink
Copy link
Author

On Mon, Sep 23, 2019 at 12:05 PM kleinpetr @.***> wrote:
Here's what worked for me (with names changed to protect the innocent): In [...]

Thank you very much, sir, I guess you have the solution. The official wiki is incomplete, in my opinion.
If you don't mind, can you tell us what values you use for these two dependencies in android\build.gradle (these values below are mine):
classpath 'com.android.tools.build:gradle:3.3.0'
classpath 'com.google.gms:google-services:4.3.0'

In addition to this issue, I've got a very strange behavior :
I defined myBackgroundMessageHandler in my class which has _firebaseMessaging.configure (I've defined it as static, of course)
But, I did NOT add onBackgroundMessage: _backgroundMessageHandler handler to _firebaseMessaging.configure.... Guess what? My background messages are well handled and working as expected !!! (at least, for those when my app is running in the background - I did not test when the app is terminated, as I'm not able to see the print out on Flutter console to confirm if it's working as well or not)

Is this an expected behavior? Or am I missing something in my understanding of this plugin (which is totally possible as well, I'm not very familiar with flutter FCM)

See my previous response: #199 (comment)

I believe this is expected behavior for notification messages with data payload. A pure data message would be handled by onBackgroundMessage (I think), but that is evidently not yet supported by the plugin.

Gregg

@JayM96
Copy link

JayM96 commented Nov 5, 2019

Hello,

I am implementing push notification currently.

My onbackgroundmessage() is not getting called even my app is terminated. How to solve the issue.

I am getting data payload inside onLaunch(). Can anyone tell me why onbackgroundmessage() might not getting called.

I have added my Application class and followed all the things as per pub documentation.

Not getting any errors.

@iapicca @mobileink Can anyone help ?

Thanks.

@kevin4dhd
Copy link

El mismo error.

@gcervantes2005
Copy link

I was having app crash, I am not using the .configure on the Main, I use on another class.

I did delay de config and worked.

Future.delayed(const Duration(milliseconds: 1000), () {
print('Configuring Firebase Messsaging');
messaging.configure(
onBackgroundMessage: onBackgroundMessageHandler,
onMessage: onMessageMessageHandler,
onLaunch: onLaunchMessageHandler,
onResume: onResumeMessageHandler
);
});

@temirfe
Copy link

temirfe commented Nov 19, 2019

can someone write Kotlin version of Application.java please?

@GENL
Copy link

GENL commented Nov 29, 2019

can someone write Kotlin version of Application.java please?

package com.example.projectname

import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService

class Application : FlutterApplication(), PluginRegistrantCallback {
    override fun onCreate() {
        super.onCreate()
        FlutterFirebaseMessagingService.setPluginRegistrant(this)
    }

    override fun registerWith(registry: PluginRegistry) {
        GeneratedPluginRegistrant.registerWith(registry)
    }
}

That's how you can do it in Kotlin

@hemalmoradiya
Copy link

I was having app crash, I am not using the .configure on the Main, I use on another class.

I did delay de config and worked.

Future.delayed(const Duration(milliseconds: 1000), () {
print('Configuring Firebase Messsaging');
messaging.configure(
onBackgroundMessage: onBackgroundMessageHandler,
onMessage: onMessageMessageHandler,
onLaunch: onLaunchMessageHandler,
onResume: onResumeMessageHandler
);
});

it's worked for me, Thanks!!!

@hb0nes
Copy link

hb0nes commented Jan 6, 2020

How do I access variables and other plugins from the background message handler?
The callback gets triggered, but everything is null.
We can literally do nothing else but print the message['data']...

@ifs-slaheddine
Copy link

How do I access variables and other plugins from the background message handler?
The callback gets triggered, but everything is null.
We can literally do nothing else but print the message['data']...

Any suggestion about this case please ? I have the same problem

@BazinC
Copy link

BazinC commented Feb 12, 2020

Hi @ifs-slaheddine, @hb0nes,
like you i had a hard time managing to make background message handler useful.
Backgound message handler runs in a different isolate, so you don't have access to the Flutter UI memory.
So far the only workaround I found is to use persistent data with flutter SharedPreferences.

Future<void> onBackgroundMessageHandler(Map<String, dynamic> message) async {
      final prefs = await SharedPreferences.getInstance();
      // SharedPreferences could have been modified in the UI isolate, so we have to fetch it from the host platform.
      await prefs.reload();
      // await prefs.getOrSetWhateverYouNeed();
}

And then make your widget (e.g your root page) overrides didChangeAppLifecycleState to process any changes that happened in background.

@override
 Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
   if (state == AppLifecycleState.resumed) {
     // Shared preferences
     final prefs = await SharedPreferences.getInstance();
     // Since the changes during background notification handler are made in a different isolate, we fetch the latest values from the host platform
     await prefs.reload();
     // Do what you want with prefs.
   }
 }

In my case, I set a flag in shared preferences to determine if the freshly resumed app need to refresh data from webservices.

Hope it will help you

@hb0nes
Copy link

hb0nes commented Feb 12, 2020 via email

@BazinC
Copy link

BazinC commented Feb 12, 2020

I did not try this way. I was not familiar with receive and send ports. Thanks for the tip :)

@iapicca
Copy link

iapicca commented Feb 28, 2020

#1775

Could everyone who still has this problem please file a new issue with the exact descriptions what happens, logs and the output of 'flutter doctor -v' please.
All system setups can be slightly different so it's always better to open new issues and reference related issues.

@JFernandoGomez
Copy link

means you need to use your custom Application class instead of the default flutter Application class

I also think this should be clearer in the documentation

@haroonkhan9426
Copy link

haroonkhan9426 commented Apr 17, 2020

My question is about the iOS part.
Does onBackMessage Callback also handles the message in the background/Terminated mode on iOS.
If yes, then how can we take actions depending on the contents of the message like showing alert etc when the app is already terminated.
@mobileink you had a very comprehensive answer above. If you can help this?

@ChoLianJiet
Copy link

Hi @ifs-slaheddine, @hb0nes,
like you i had a hard time managing to make background message handler useful.
Backgound message handler runs in a different isolate, so you don't have access to the Flutter UI memory.
So far the only workaround I found is to use persistent data with flutter SharedPreferences.

Future<void> onBackgroundMessageHandler(Map<String, dynamic> message) async {
      final prefs = await SharedPreferences.getInstance();
      // SharedPreferences could have been modified in the UI isolate, so we have to fetch it from the host platform.
      await prefs.reload();
      // await prefs.getOrSetWhateverYouNeed();
}

And then make your widget (e.g your root page) overrides didChangeAppLifecycleState to process any changes that happened in background.

@override
 Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
   if (state == AppLifecycleState.resumed) {
     // Shared preferences
     final prefs = await SharedPreferences.getInstance();
     // Since the changes during background notification handler are made in a different isolate, we fetch the latest values from the host platform
     await prefs.reload();
     // Do what you want with prefs.
   }
 }

In my case, I set a flag in shared preferences to determine if the freshly resumed app need to refresh data from webservices.

Hope it will help you

When i try to run SharedPreferences in onBackgroundMessageHandler i will have the following error

MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences)

@sathishmscict
Copy link

sathishmscict commented Jul 23, 2020

Hi @ifs-slaheddine, @hb0nes,
like you i had a hard time managing to make background message handler useful.
Backgound message handler runs in a different isolate, so you don't have access to the Flutter UI memory.
So far the only workaround I found is to use persistent data with flutter SharedPreferences.

Future<void> onBackgroundMessageHandler(Map<String, dynamic> message) async {
      final prefs = await SharedPreferences.getInstance();
      // SharedPreferences could have been modified in the UI isolate, so we have to fetch it from the host platform.
      await prefs.reload();
      // await prefs.getOrSetWhateverYouNeed();
}

And then make your widget (e.g your root page) overrides didChangeAppLifecycleState to process any changes that happened in background.

@override
 Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
   if (state == AppLifecycleState.resumed) {
     // Shared preferences
     final prefs = await SharedPreferences.getInstance();
     // Since the changes during background notification handler are made in a different isolate, we fetch the latest values from the host platform
     await prefs.reload();
     // Do what you want with prefs.
   }
 }

In my case, I set a flag in shared preferences to determine if the freshly resumed app need to refresh data from webservices.
Hope it will help you

When i try to run SharedPreferences in onBackgroundMessageHandler i will have the following error

MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences)

Define your shared preference plugin in application level. like
image

Using above method you can show notification also. Am implemented in my project now am able to handle data messages and show notification from background_handler.Its working fine.

@CoderJava
Copy link

Hi @ifs-slaheddine, @hb0nes,
like you i had a hard time managing to make background message handler useful.
Backgound message handler runs in a different isolate, so you don't have access to the Flutter UI memory.
So far the only workaround I found is to use persistent data with flutter SharedPreferences.

Future<void> onBackgroundMessageHandler(Map<String, dynamic> message) async {
      final prefs = await SharedPreferences.getInstance();
      // SharedPreferences could have been modified in the UI isolate, so we have to fetch it from the host platform.
      await prefs.reload();
      // await prefs.getOrSetWhateverYouNeed();
}

And then make your widget (e.g your root page) overrides didChangeAppLifecycleState to process any changes that happened in background.

@override
 Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
   if (state == AppLifecycleState.resumed) {
     // Shared preferences
     final prefs = await SharedPreferences.getInstance();
     // Since the changes during background notification handler are made in a different isolate, we fetch the latest values from the host platform
     await prefs.reload();
     // Do what you want with prefs.
   }
 }

In my case, I set a flag in shared preferences to determine if the freshly resumed app need to refresh data from webservices.
Hope it will help you

When i try to run SharedPreferences in onBackgroundMessageHandler i will have the following error
MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences)

Define your shared preference plugin in application level. like
image

Using above method you can show notification also. Am implemented in my project now am able to handle data messages and show notification from background_handler.Its working fine.

Thank you. It's really worked to me.

@beingcoderr
Copy link

beingcoderr commented Jul 25, 2020

import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService

class Application : FlutterApplication(), PluginRegistrantCallback {
    override fun onCreate() {
        super.onCreate()
        FlutterFirebaseMessagingService.setPluginRegistrant(this)
    }

    override fun registerWith(registry: PluginRegistry) {
        GeneratedPluginRegistrant.registerWith(registry)
    }
}

Throws error:

mismatch: inferred type is PluginRegistry but FlutterEngine was expected

@Aeonko
Copy link

Aeonko commented Aug 2, 2020

    GeneratedPluginRegistrant.registerWith(registry)

cast registry to (FlutterEngine)
@OverRide
public void registerWith(PluginRegistry registry) {
GeneratedPluginRegistrant.registerWith((FlutterEngine) registry);
}

@saravananmnm
Copy link

Again i need to write the same code, which was written for receive FCM. But outside the class.

`Future myBackgroundMessageHandler(Map<String, dynamic> message) async {
FirebaseMessaging firebaseMessaging = new FirebaseMessaging();
AndroidInitializationSettings android = new AndroidInitializationSettings(
'@mipmap/ic_launcher'); //@mipmap/ic_launcher
IOSInitializationSettings ios = new IOSInitializationSettings(
onDidReceiveLocalNotification: onDidReceiveLocalNotification);
var initSettings = new InitializationSettings(android, ios);
var flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
flutterLocalNotificationsPlugin.initialize(initSettings);
firebaseMessaging.requestNotificationPermissions(
const IosNotificationSettings(sound: true, alert: true, badge: true));
String val = '';
String aNotificationType = '';
String expiryTime = '';

if (Platform.isAndroid) {
print('${message['data']['message']}');
val = message['data']['message'];
aNotificationType = message['data']['notificationType'];
expiryTime = message['data']['expiryTime'];
} else if (Platform.isIOS) {
print('${message['notification']['message']}');
val = message['notification']['message'];
aNotificationType = message['notification']['notificationType'];
expiryTime = message['notification']['expiryTime'];
}
print("Notification Type ---> " + aNotificationType);
print('');
print('FireBase Notification Came');
print('
');

var androidPlatformChannelSpecifies = new AndroidNotificationDetails(
"CA", "Courier Alliance", "Courier Alliance",
importance: Importance.Max,
groupKey: 'iex',
groupAlertBehavior: GroupAlertBehavior.All,
priority: Priority.Max,
visibility: NotificationVisibility.Public,
color: Colors.blue,
channelShowBadge: true,
when: Utils.parseStringToInt(expiryTime),
autoCancel: true,
enableVibration: true,
styleInformation: BigTextStyleInformation(''),
largeIcon: DrawableResourceAndroidBitmap('@mipmap/ic_launcher'),
playSound: true,
sound: RawResourceAndroidNotificationSound('new_auction_notification'));
var iOSPlatformChannelSpecifics = new IOSNotificationDetails(
presentAlert: true, presentSound: true, presentBadge: true);
var platformChannelSpecifics = new NotificationDetails(
androidPlatformChannelSpecifies, iOSPlatformChannelSpecifics);

await flutterLocalNotificationsPlugin.show(
    0, 'Courier Alliance', val, platformChannelSpecifics,
    payload: aNotificationType);

return Future.value(flutterLocalNotificationsPlugin);
}`

@firebase firebase locked and limited conversation to collaborators Aug 19, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
impact: crowd Affects many people, though not necessarily a specific customer with an assigned label. (P2) type: documentation Improvements or additions to documentation
Projects
None yet