Migrating Push notification from firebase_messaging 6 to firebase_messaging 10+ - flutter

I am coming from an old Firebase_messaging plugin 6 + to the newer Firebase_messenger plugin 10 +, I am able to do most of the thing but can't get the message data, I want to convert this code from the older plugin to a newer one and use methods like configure launch and onResume.I can receive the push notifications, foreground and background information about the message but can't read it.
class _ChatScreenState extends State<ChatScreen> {
#override
void initState() {
super.initState();
final fbm = FirebaseMessaging();
fbm.requestNotificationPermissions();
fbm.configure(onMessage: (msg) {
print(msg);
return;
}, onLaunch: (msg) {
print(msg);
return;
}, onResume: (msg) {
print(msg);
return;
});
}
What I have done so far
Under AndroidManifest.xml added
<meta-data android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="high_importance_channel" />
On main.dart
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print('Handling a background message ${message.messageId}');
}
Future <void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
Here is where I want to get the data from the server
class _ChatScreenState extends State<ChatScreen> {
#override
void initState() {
FirebaseMessaging.instance
.getInitialMessage()
.then((RemoteMessage message) {
if (message != null) {
print(message);
}
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
print('Message data: ${message.data}');
if (message.notification != null) {
print(
'Message also contained a notification: ${message.notification}');
}
});
});
super.initState();
}
On Debug while the App is in the foreground
D/FLTFireMsgReceiver(15437): broadcast received for message
I/flutter (15437): Got a message whilst in the foreground!
I/flutter (15437): Message data: {}
I/flutter (15437): Message also contained a notification: Instance of 'RemoteNotification'
On Background
D/FLTFireMsgReceiver(15437): broadcast received for message
W/FirebaseMessaging(15437): Missing Default Notification Channel metadata in AndroidManifest. Default value will be used.
I/flutter (15437): Handling a background message 0:1624718321445677%ba7e1d8bba7e1d8b
Although the Notification Text and body is fine in the notification window but can't get the same info in the debug screen, it returns empty. Also is my implementation correct?

With a little bit of searching I believe the missing piece was flutter_local_notifications: ^5.0.0+4 The changes I have made in
main.dart
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// 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.
await Firebase.initializeApp();
print('Handling a background message ${message.messageId}');
}
const AndroidNotificationChannel channel = const AndroidNotificationChannel(
//for notificaiton initialization
'high_importance_channel', // id
'High Importance Notifications', // title
'This channel is used for important notifications.', // description
importance: Importance.high,
playSound: true,
);
//initialize plugin
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
//for background messaging
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
//Local Notification implementation
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
//for firebase plugin and messaging required
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true, badge: true, sound: true);
runApp(MyApp());
}
In the class where I want the notification
class _ChatScreenState extends State<ChatScreen> {
#override
void initState() {
//foreground messaging
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification notification = message
.notification; //assign two variables for remotenotification and android notification
AndroidNotification android = message.notification?.android;
if (notification != null && android != null) {
print(message);
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id, channel.name, channel.description,
color: Colors.blue,
playSound: true,
icon: '#mipmap/ic_launcher'),
),
);
print(notification.title);
print(notification.body);
}
});
//Do when the user taps the notification
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
//same as above
});
super.initState();
}
Would be nice if some one update this process for IOS as I don't have a developer Id to test it.

Related

Futter background notification with Navigation

i listen to the notification as below
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await FirebaseMessaging.instance.getToken();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyProject(
appRouter: AppRouting(),
));
}
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// 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.
await Firebase.initializeApp();
NotificationServices ntService = NotificationServices();
print("Handling a background message: ${message.data['room']}");
ntService.showNotification(message);
}
void showNotification(RemoteMessage message) {
flutterLocalNotificationsPlugin.show(
message.data.hashCode,
message.data['title'],
message.data['body'],
NotificationDetails(
android: AndroidNotificationDetails(
NotificationServices.channel.id,
NotificationServices.channel.name,
channelDescription: NotificationServices.channel.description,
//channel.description,
icon: 'launch_background',
playSound: true,
// other properties...
),
),
payload: message.data['room']);
}
}
await flutterLocalNotificationsPlugin.initialize(
initialzationSettings,
onDidReceiveNotificationResponse: onSelectNotification,
onDidReceiveBackgroundNotificationResponse: onSelectNotification,
);
in the onSelectNotification
i get the roomID and then navigate to it but to do so i had to but it in the homepage screen in order to set the user and room data so i get an error i should use in the top level function in the main.dart and it is working fine with the foreground notification but for the background it just open the app so any ideas of how should i do it

firebaseMessagingBackgroundHandler not triggered when app is in background

I assumed I followed all the steps to handling background notifications from firebase in flutter. I have created a top-level function that I am expecting to be triggered whenever a notification comes in. However, the function is never triggered.
Here's the top-level background handler function that exists in my home page widget but outside the class:
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// ignore: avoid_print
print('A background message just showed up : ${message.messageId}');
// update SQLite
var result = await PageService.instance
.add(PageService.instance.convertToPage(message.data));
print('added to db: ${result}');
}
Here is my home page init state that calls a function to initialize firebase messgaging:
#override
void initState() {
super.initState();
_initializeFirebaseMessaging();
}
And then here is the _initializeFirebaseMessaging function that is defined in the home page class as well:
void _initializeFirebaseMessaging() {
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
print('new notification arrived');
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
// update SQLite
var result = await PageService.instance
.add(PageService.instance.convertToPage(message.data));
print('added to db: ${result}');
if (notification != null && android != null) {
// show notification
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
color: Colors.blue,
playSound: true,
icon: '#mipmap/ic_launcher',
),
));
}
});
}
The onmessage.listen works fine as I get notificationd and handle them while I'm in the app, but the background handler is not triggered at all.
I would appreciate any help!
You have to call FirebaseMessaging.onBackgroundMessage inside your main() not in initState()
try: after main func
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
...
}
and remove WidgetsFlutterBinding.ensureInitialized(); in firebaseMessagingBackgroundHandler
onMessage handles foreground notification
onMessageOpenedApp handles background notification
Instead of using onMessage for handling Background notifications, add another function block of FirebaseMessaging with onMessageOpenedApp, if you want notification while the app is in background, like this:
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message){...})
onMessage only works when the app is in foreground.
If you want notification while the app is in the terminated state you can use:
FirebaseMessaging.instance.getInitialMessage().then((message){...})
Update your firebaseMessagingBackgroundHandler to this:
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
// ignore: avoid_print
print('A background message just showed up : ${message.messageId}');
// update SQLite
var result = await PageService.instance
.add(PageService.instance.convertToPage(message.data));
print('added to db: ${result}');
}
And finally, the main() should look like this:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Add Firebase Initialization:
await Firebase.initializeApp();
// This will initialize a new Firebase instance.
// Background message handler
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
runApp(const App());
}
You should call FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler); from within main(), not from Homepage initState(){}.

FirebaseMessaging.instance.getInitialMessage() is not called in terminated state

FirebaseMessaging.instance.getInitialMessage() is not called in terminated state when we click notification that contains message data like this ,instead it is displaying screen from splash screen
{
"registration_ids": [
""
],
"data": {
"title": "Flutter9 Object Notification.3...",
"body": "this is flutter Data Object notification test message from",
"android_channel_id": "dgdgsdfgs",
},
"android":{
"priority":"high"
}
}
Given that you not displayed how you setup your fcm , here is how to go about it
/// Create a [AndroidNotificationChannel] for heads up notifications
late AndroidNotificationChannel channel;
/// Initialize the [FlutterLocalNotificationsPlugin] package.
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
/// To verify things are working, check out the native platform logs.
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// 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.
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
print('Handling a background message ${message.messageId}');
}
in main.dart
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
// setup displaying notifications
channel = const AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title// description
importance: Importance.high,
);
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
/// Create an Android Notification Channel.
///
/// We use this channel in the `AndroidManifest.xml` file to override the
/// default FCM channel to enable heads up notifications.
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
/// Update the iOS foreground notification presentation options to allow
/// heads up notifications.
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
handle message call this in your initstate
FirebaseMessaging.instance
.getInitialMessage()
.then((RemoteMessage? message) {
if (message != null) {
// do message things
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
showMessage(message: message);
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
),
iOS: IOSNotificationDetails(subtitle: notification.title)));
}
});
incase you need to navigate on receiving a message
void showMessage({required RemoteMessage message}) async {
if (message.data['route'] == "mypage") {
var myparams = message.data['payload'];
Navigator.of(context).push(MaterialPageRoute(
fullscreenDialog: true,
builder: (_) {
return MyPage(
params:myparams,
);
})); ...
With FCM, you can send two types of messages to clients:
Notification messages, are sometimes thought of as "display messages." These are handled by the FCM SDK automatically.
Data messages, which are handled by the client app. "
https://firebase.google.com/docs/cloud-messaging/concept-options
What you are trying to send is a data message, my best guess is that the firebase SDK does not recognize the data message so it does not trigger the firebase message event listener. Your notification must have a 'notification' field for firebase SDK to listen to.

Flutter Firebase Cloud Messaging how to Auto Dismiss/Cancel a notification?

I have successfully managed to receive FCM messages(on my mobile) via the console as well as from my NodeJs server. But how may I send & receive an FCM message that will arrive at my phone, do some tasks and then auto Cancel/Dismiss by itself?
Is this possible in Flutter with FCM?
In Android we use to have public Notification.Builder setTimeoutAfter (long durationMs)
Its more for just pinging the client app... and retrieving some data from the Apps local storage. Since it can be done automatically i want to do it without troubling the user.
These are the steps to accomplish the following
receiving a notification without disturbing the user(silently without any Alert in the system tray)
let localNotification Pkg start the progress Notification
do a background task and when finished
cancel the notification via LocalNotifications Pkg
Make sure you have the following in your .yaml file... at the time of solving this I had the following versions:
firebase_messaging: ^11.1.0
firebase_core: ^1.10.0
flutter_local_notifications: ^9.1.
For the Local Notification Package lets make a class to use its services
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class LocalNotificationService {
static final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin();
static void initialize(BuildContext context) {
final InitializationSettings initializationSettings = InitializationSettings(
android: const AndroidInitializationSettings("#mipmap/your_icon"));
_notificationsPlugin.initialize(initializationSettings);
}
//=================================================
//==============this is the update notification
static Future<void> showProgressNotification() async {
const int maxProgress = 5;
for (int i = 0; i <= maxProgress; i++) {
await Future<void>.delayed(const Duration(seconds: 1), () async {
final AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails('progress channel', 'progress channel',
channelDescription: 'progress channel description',
channelShowBadge: false,
importance: Importance.max,
priority: Priority.high,
playSound: false,
showProgress: true,
maxProgress: maxProgress,
progress: i);
final NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
await _notificationsPlugin.show(
0,//I use this id to cancel it from below method
'progress notification title',
'progress notification body',
platformChannelSpecifics,
payload: 'item x');
});
}
}
//=========================and this is for the ProgressNotification to be cancelled
static Future<void> cancelNotification() async {
await _notificationsPlugin.cancel(0);
}
}//end of class
Make your you initialize it in the init method of your Widget
#override
void initState() {
// TODO: implement initState
super.initState();
LocalNotificationService.initialize(context);
}
And lastly... this is how your Main() and top handler will look
//Receive message when app is in background/minimized
//THIS IS THE TOP LEVEL HANDLER.. as it is outside the scope of main()
Future<void> backgroundHandler(RemoteMessage message) async{
print("from the Background Handler Top Function()..............");
print(message.data.toString());
//now for the localNotification to take over
await LocalNotificationService.showProgressNotification();
await Future<void>.delayed(const Duration(seconds: 2));//faking task delay
await LocalNotificationService.cancelNotification();//by default I have made id=0
}
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(backgroundHandler);//this has to be a TOP LEVEL METHOD
runApp(MyApp());
}
And on the Server side while sending the notification make sure there is only data{}... see #Junsu Cho answer
ios is impossible
android is possible, remove notification payload
{
"to": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."
"notification":
{
"title": "title"
}
"data":
{
"data" : "data"
}
}
to
{
"to": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."
"data":
{
"data" : "data"
}
}

How to display multiple notifications using flutter plugin : flutter_local_notifications

Here is my code below :
const AndroidNotificationChannel notificationChannel = AndroidNotificationChannel(
'high_importance_channel',
'high importance Notificaion',
'this channel is used for import notification',
importance: Importance.high,
playSound: true,
);
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
Future<void> firebaseBackgroundMessageHandler(RemoteMessage message) async {
await firebaseMessageHandler(message);
}
Future<void> firebaseForegroundMessageHandler(RemoteMessage message) async {
await firebaseMessageHandler(message);
}
Future<void> firebaseMessageHandler(RemoteMessage message) async {
await Firebase.initializeApp();
try {
flutterLocalNotificationsPlugin.show(
message.notification.hashCode,
message.data["title"].toString(),
message.data["body"].toString(),
NotificationDetails(
android: AndroidNotificationDetails(notificationChannel.id, notificationChannel.name, notificationChannel.description),
));
} catch (_err) {}
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(firebaseBackgroundMessageHandler);
FirebaseMessaging.onMessage.listen(firebaseForegroundMessageHandler);
//FirebaseMessaging.onMessageOpenedApp.listen(firebaseMessageHandler);
final AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('e_app');
final InitializationSettings initializationSettings = InitializationSettings(android: initializationSettingsAndroid);
await flutterLocalNotificationsPlugin.initialize(initializationSettings);
await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(notificationChannel);
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(alert: true, badge: true, sound: true);
}
The code is working fine for a single notification but whenever I send multiple notifications to android, it shows only the last notification in the status bar.
Could anyone help me about
how to show multiple notifications in the status bar using the flutter local notification plugin?
If you want to group your notifications, there is an option to do that in flutter_local_notifications.
To do this, you can add the following in your code
for ios : threadIdentifier in IOSNotificationDetails
for android : groupChannelId, groupChannelName, groupChannelDescription,
You can find these in the docs here: https://pub.dev/packages/flutter_local_notifications#displaying-a-notification under 'Grouping notifications'.
Basically notifications under same threadId or groupId will all be grouped and shown