We have implemented firebase notification in a flutter app running on android. Below is the function that we have defined for handling background messages
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
#pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
print('Handling a background message ${message.messageId}'); }
below is the notification body that is being sent
{
"to" : "eYQ8cukfTzW:APA91b,
"data" : {
"body" : "Notification Body",
"title": "Notification Title",
"key_1" : "Value for key_1",
"key_2" : "Value for key_2"
}
}
We are using firebase_messaging: ^14.1.0
My call handler is above my main function
#pragma('vm:entry-point') //this is used for where indicates this function not removed while releasing
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print('Handling a background message ${message.messageId}');
}
Inside my main.dar
I am calling this like this
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); //for handling background
And for foreground messages like this
FirebaseMessaging.instance.setForegroundNotificationPresentationOptions( alert: true, badge: true, sound: true);
it works perfectly fine while the app is in the foreground. However, if the app is closed then the notification is not going to the background message handler
The notifications appear when the app is running. However, if the app is closed, the app does not catch any notifications. it seems like the background method does not get invoked when notifications are received. Can anyone please help how to fix this?
Related
I'm handling FCM in my flutter app and i want only to receive one scheduled notification if my app is terminated and i don't want to receive it if app is working in the background now.
This is my code and when i'm testing i'm receiving this notification if my app is in background or is terminated.
Future<void> background_handler(RemoteMessage message) async {
await Firebase.initializeApp();
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: FirebaseOptions(apiKey:
"apikey", appId: "appid", messagingSenderId: "senderid", projectId: "projid"));
//handle msg in terminate case
RemoteMessage? message = await FirebaseMessaging.instance.getInitialMessage();
if(message != null){
PushNotification notification = new PushNotification(title:
message.notification?.title?? "Title",
body: message.notification?.body?? "Body", dataTitle: message.data["title"],
dataBody: message.data["body"] );
}
FirebaseMessaging.onBackgroundMessage(background_handler);
runApp(const MyApp());
}
How I can change this code to make app only receive this notification if it's terminated, or it's not possible to separate the background and terminated cases?
I don't think that's possible since onBackgroundMessage handles messages when the app is in both background and terminated state. Also, Flutter can only detect if the app is in background but not when it's terminated.
I'm using cloud function to send notifications by using device tokens.
const payload: admin.messaging.MessagingPayload = {
notification: {
title: `updatedBy: ${updatedBy}`,
body: `nova messagem ${increment}`,
},
}
return fcm.sendToDevice(userToken, payload, options).then((value) => {
console.log(value.results);
}).catch((error) => {
console.log(error);
})
But, all notification got pilled, I wanna so much to administrate these notification that stay stuck in system tray, is there anyway I can moderate notifications with such "complexity" using these plugins? (Local push Notification, FCM, cloudfunctions) without using native code (kotlin, android)? because I don't know how the android structure work...
I can use cancelAll (of Local push notification)
FlutterLocalNotificationsPlugin.cancelAll()
from Local Push Notification: https://pub.dev/documentation/flutter_local_notifications/latest/
But is too inflexible
I can also use cancel notification using an specific "ID", but I don't know how to get that notification "id"
FlutterLocalNotificationsPlugin.cancel(notificationId)
so, How can solve this issue?
In that way I can wisely remove whatever notification I want,
But basically, I just wanna collapse notification that is the same type, like on Whatsapp, when a person send you more than 1 message, and in the system tray, there's only one notification saying that "he" sent more than 1 message (and not a heavy pile of notification).
I just looked at https://github.com/chetdeva/collapsing-notifications/tree/a5e8867e24f53ff8caca761c7faa13242c607dba
This github code is for the article https://medium.com/fueled-engineering/collapsing-fcm-notification-like-a-pro-102a4946b350
would be perfect if it would not too difficult to implement in a flutter project, I also looked at every post here, in stackoverflow, seens that no one yet ever answered this question
[EDIT]: the reason I can't use
FlutterLocalNotificationsPlugin.cancelAll()
It's because I'm using fcm method to listen for notifications, I tried to put that method in some of fcm listener but not succeed.
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print('on message $message');
},
onResume: (Map<String, dynamic> message) async {
print('on resume $message');
},
onLaunch: (Map<String, dynamic> message) async {
print('on launch $message');
},
);
Use the "collapse key" mentioned in the docs: https://firebase.google.com/docs/cloud-messaging/concept-options#ttl
I am developing a react-native messaging app with Expo. Every time a user receives a new message, I send a notification from my server.
Is there any way to not display the notification if the app is currently open?
Right now I am using this as soon as the notification is received:
Notifications.dismissNotificationAsync(notification.notificationId);
But there is a 0.5 second delay where the notification has time to appear in the tray and trigger a sound before it gets dismissed. I would like to not show it at all.
When a notification is received while the app is running, using setNotificationHandler you can set a callback that will decide whether the notification should be shown to the user or not.
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
});
When a notification is received, handleNotification is called with the incoming notification as an argument. The function should respond with a behavior object within 3 seconds, otherwise the notification will be discarded. If the notification is handled successfully, handleSuccess is called with the identifier of the notification, otherwise (or on timeout) handleError will be called.
The default behavior when the handler is not set or does not respond in time is not to show the notification.
If you don't use setNotificaitonHandler, the new notifications will not be displayed while the app is in foreground.
So you can simply set setNotificationHandler to null when your app is initialized.
Notifications.setNotificationHandler(null);
See Documentaition
The answer is yes to your question
Is there any way to not display the notification if the app is
currently open?
The default behavior of Notification in Expo is not to show notification if the App is in foreground. You must have implemented Notifications.setNotificationHandler similar to the following code -
// *** DON'T USE THE FOLLOWING CODE IF YOU DON'T WANT NOTIFICATION TO BE DISPLAYED
// WHILE THE APP IS IN FOREGROUND! ***
// --------------------------------------------------
// Sets the handler function responsible for deciding
// what to do with a notification that is received when the app is in foreground
/*
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: false,
}),
});
*/
If you don't use setNotificaitonHandler, the new notifications will not be displayed while the app is in foreground.
Use below code snippet. It works on press notification.
_handleNotification = async (notification) => {
const {origin} = notification;
if (origin === ‘selected’) {
this.setState({notification: notification});
}
//OR
if (AppState.currentState !== 'active') {
this.setState({notification: notification});
}
}
I assume you setup a simple FCM - Firebase cloud messaging
And use that to push messages to the client?
The official Expo guide has a section for receiving-push-notifications
This is the actual workflow of FCM (weird can be called as a common issue) that it'll handle the notifications by itself when the application is in the foreground.
The solution which i did for my project was to create a custom notification JSON rather than using their default template which won't be parsed by FCM.
{
"hello":" custom key and value",
"message":{
"SampleKey":"Sample data",
"data":{
"SampleKey" : "Sampledata",
"SampleKey2" : "great match!"},
}}
In console you can add your own custom JSON objects, and when you get the notification parse the notification by using these objects, then you will be able to override that issue.
You can also add a channel for the request to categorize your notifications
this.createNotificationListeners = firebase.notifications()
.onNotification((notification) => {
let{ hello,data,message} = notification;
});
I'm using Firebase Cloud Messaging to send notifications and ShortcutBadger to update the unread messages count on our app badge. I've extended the FirebaseMessagingService class and implemented the override method onMessageReceived, which seems to be working fine since I get notifications when my app is in the background or killed. However, when I add the ShortcutBadger call inside this method, I no longer receive notifications and the ShortcutBadger call does not work.
ShortcutBadger requires a Context and, since FirebaseMessagingService extends Service, it is a context, which is why I simply pass this. I also have ShortcutBadger working in other parts of my code, so I know it works.
Any suggestions would be appreciated.
public class Notifications extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
ShortcutBadger.applyCount(getApplicationContext(), 1); // <-- now is working
}
}
EDIT: SERVER SIDE CODE
Here's the server-side code that sends the Firebase message.
Notification notification = new Notification.Builder(null)
.title(msg.getTitle())
.badge(msg.getBadge())
.body(msg.getMessage())
.build();
Message message = new Message.Builder()
.timeToLive(timeToLive)
.delayWhileIdle(true)
.notification(notification)
.addData("text", msg.getMessage())
.addData("title", msg.getTitle())
.addData("line1", msg.getSubtitle())
.addData("badge", String.valueOf(msg.getBadge()))
.addData("qummute_notification", "true")
.build();
Result result = send(message, deviceKey, retries);
It's been suggested that I send the message without the Notification payload. Will try that and see what happens.
Have you made sure that you are not calling ShortcutBadger.removeCount(Context context) while testing?
Try using ShortcutBadger.applyCountOrThrow(Context context, int badgeCount) to see if any exceptions are thrown.
The onMessageReceived(RemoteMessage remoteMessage) is called on a background thread. Perhaps the phone/launcher you are testing on is using a badge that cannot be created and shown from a background thread.
You need to handle this situation from server side.Remove Notification from your JSON and Use data.
Remove Notification Hole object,
"notification": {
"title": "My Title if any",
"body": "location",
"sound": "default"
},
From where you are sending the notification you need to change that code,
For Server side code should be like below then your onMessageReceived method call even if app is in the background or killed.
{
"registration_ids": [
"cBbGxlj5JRI:APA91bFRLp3tDoBo_P5lGYGUZ4F6iJ55yBJq4GInL7RVV2asHR5PovqnMX-ekIdl_xOYyRyJmSe3SkxYMa3mzIvqml3MmLYZUh8JWygixIebhzhb_l4xv0Q-v3werKl_UO069G1uOyvq"
],
"data": {
"title": "alt_ctgry - Vehicle Number - extra detail(if any)",
"body": "location"
},
"priority": "high"
}
if you want to create notification then write your own code for notification.
For Notification data access use below code,
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Log.e("FCM", "Call onMessageReceived\n" + remoteMessage + "");
String title = remoteMessage.getData().get("title");
String body = remoteMessage.getData().get("body");
createNotification(title, body);
}
which seems to be working fine since I get notifications when my app is in the background or killed
No, onMessageReceived is called when your app in foreground but not background IF you have notification payload.
In your server side code you add notification payload, so previously your onMessageReceived also was not called when app in background but notification was shown by OS from your payload.
If you want to handle your push all the time you need to remove notification payload, but remember that it could break pushes on iOS if you have an iOS app.
I use Firebase Cloud Messaging (FCM) HTTP Legacy API protocol to send push notifications in JSON to android mobile devices. For the client side I use react-native-fcm library.
The aim is to send the notification to the particular devices when the application is in 3 states:
1) running
2) background running
3) killed
According to the documentation for FCM there are 3 different types of messages which can be sent via FCM service:
1) notification (has predefined fields)
2) data (set whatever fields you want)
3) mixed (notification + data).
The logic of listening the event for incoming message on the client side using react-native-fcm is the next:
this.notificationEmitterSubscription = FCM.on(FCMEvent.Notification, notif => {
if(notif && notif.fcm){
//received from Firebase
if(!notif.local_notification && notif.title){
let badge = parseInt(notif.badge);
FCM.setBadgeNumber(badge);
this.showNotification(notif.title, notif.body, badge);
}
//notification is clicked
if(notif.opened_from_tray){
FCM.setBadgeNumber(0);
this.executeNavigateAction(notif.fcm.action); //this method just navigates user to a particular screen in the application
}
}
});
Show notification method is implemented in this way:
showNotification(title, body, badge) {
FCM.presentLocalNotification({
body: body,
priority: "high",
title: title,
sound: "default",
large_icon: "ic_launcher",// Android only
icon: "ic_launcher",
show_in_foreground :true, /* notification when app is in foreground (local & remote)*/
vibrate: 300, /* Android only default: 300, no vibration if you pass null*/
lights: true, // Android only, LED blinking (default false)
badge: badge,
local: true,
click_action: NAV_SCREEN_NAME
});
}
notif.title, notif.body and notif.badge are the fields which are set in data section of the message when sending it via FCM API. In other word the message is sent in the (3) mixed form:
{
"registration_ids" : ["FCM_device_token_1", "FCM_device_token_2"],
"notification" :
{
"title" : "fcm notification message title",
"body" : "fcm notification message body",
"badge" : 111
},
"data" :
{
"title" : "fcm data message title",
"body" : "fcm data message body",
"badge" : 222
}
}
If the message is sent as (1) notification (without "data" section in the message, in this case some changes in the reading the fields are necessary, to change notif.title -> notif.fcm.title, but this is not the main point in the question) or mixed (3) then the listener for the notification is NOT triggered when application is (2) background running and (3) killed. As a result, the badge number is not set. BUT despite the fact that the method showNotification(title, body, badge) is not called (because the event listener is not triggered) the message IS shown. It seems that react-native-fcm has internal implementation for this situation to show (1) notification and (3) mixed messages automatically when application is not running. In other words, the listener IS called for (1) notification and (3) mixed messages only when the application is (1) running and IS NOT called when the application is in the (2) background or (3) killed and does NOT show the badge number. However, the message itself IS shown for all situations.
Another approach is to send a (2) data message. This type of FCM message triggers the listener (notificationEmitterSubscription) for all states of the application: (1) running and (2) background running and (3) killed. As a result, badge number is set in all these states. However, despite the fact that method showNotification(title, body, badge) is called whenever a data FCM message is received, method FCM.presentLocalNotification does NOT display the message if the application is killed.
Thus, in few words, I have a question.
How to:
EITHER display a badge number when (1) notification or (3) mixed message is received and the application is in (2) background running or (3) killed
OR display a (2) data message when the application is (3) killed?
Thank you!
The solution has been found. The statement is that: there is no code running if the application is killed, so the messages is handled and displayed out of your code. The message has to be set in the next format to be shown for the killed status:
{
"registration_ids" : ["FCM_token_1", "FCM_token_2"],
"data" :
{
"custom_notification" :
{
"title" : "FCM test title",
"body" : "FCM test body"
},
badge : 1
}
}
In your react-native application in the notification handler the notification is received as a json value of notif.custom_notification property. So, the code looks like this:
this.notificationEmitterSubscription = FCM.on(FCMEvent.Notification, notif => {
if(notif && notif.fcm){
//received from Firebase
if(!notif.local_notification && notif.custom_notification){
let message = JSON.parse(notif.custom_notification);
let body = message.body;
let title = message.title;
let badge = parseInt(notif.badge);
FCM.setBadgeNumber(badge);
this.showNotification(title, body, badge);
}
//notification is clicked
if(notif.opened_from_tray){
FCM.setBadgeNumber(0);
this.executeNavigateAction(notif.fcm.action);
}
}
});
The issue can be solved as a resolved one.