I'm trying to setup my mobile app, so that when a user gets a FCM message, when they click on it, I can use data within the message to route them to appropriate screen.
My FCM message looks like this:
const fcmMessage = {
notification: {
title: title,
body: message
},
data:{
type:'Chat',
Name: 'Mike',
body:'test'
},
android: {
notification: {
title:title,
body: message,
channel_id:'high_importance_channel'
}
},
token: msgToken,
};
then within my main() method, I am initializing the Flutter_Local_notifications as per the code snippet below.
The issue is when I click on the notification, the payload is always an empty string?
These are the code lines that perform this. Why is the NotificationResponse.payload empty string?
ultimately, I need access the "data" object in the FCM message.
void onDidReceiveNotificationResponse(NotificationResponse notificationResponse) async {
print(notificationResponse.payload);
}
Here is the full main() method.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// Set the background messaging handler early on, as a named top-level function
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
if (!kIsWeb) {
channel = const AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title/
importance: Importance.high,
);
}
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
var initializationSettingsAndroid =
AndroidInitializationSettings('#mipmap/ic_launcher');
var initializationSettingsIOs = DarwinInitializationSettings();
var initSettings = InitializationSettings(
android: initializationSettingsAndroid, iOS: initializationSettingsIOs);
void onDidReceiveNotificationResponse(NotificationResponse notificationResponse) async {
print(notificationResponse.payload);
}
await flutterLocalNotificationsPlugin.initialize(initSettings,onDidReceiveNotificationResponse: onDidReceiveNotificationResponse,);
/// 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);
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
print('Message data: ${message.data}');
if (message.notification != null) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
FlutterLocalNotificationsPlugin s = FlutterLocalNotificationsPlugin();
s.show(
notification.hashCode,
notification?.title,
notification?.body,
NotificationDetails(
android: AndroidNotificationDetails(channel.id, channel.name,
icon: 'launch_background',
channelDescription: channel.description,
importance: Importance.max,
priority: Priority.high,
ongoing: true,
styleInformation: BigTextStyleInformation('')),
),
);
}
});
runApp(MyApp());
}
UPDATE, found what I needed. In the LocalNotification show method, we can add the payload attribute and set it to whatever part of the message.
For my use case, I encode the message.data , and then in the didReceive method, I can decode back to JSON object and use as needed.
s.show(
payload: jsonEncode(message.data),
notification.hashCode,
notification?.title,
notification?.body,
NotificationDetails(
android: AndroidNotificationDetails(channel.id, channel.name,
icon: 'launch_background',
channelDescription: channel.description,
importance: Importance.max,
priority: Priority.high,
ongoing: true,
styleInformation: BigTextStyleInformation('')),
),
);
Related
I am using flutter local notification in my application. I could not able to navigate to a specific page by clicking the local notification when the application is in background.
It gives me the error:
_AssertionError ('package:flutter_local_notifications/src/platform_flutter_local_notifications.dart': Failed assertion: line 1018 pos 12: 'callback != null': The backgroundHandler needs to be either a static function or a top
level function to be accessible as a Flutter entry point.)
This is my code:
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
Future<void> listiningToNotification() async {
FirebaseMessaging.onMessageOpenedApp
.listen((message) => notificationOnClick(message));
}
Future<void> localNotification(RemoteMessage message) async {
AndroidNotificationChannel channel = AndroidNotificationChannel(
"local_notification",
message.data["booking_id"],
description: message.data["task_id"],
importance: Importance.max,
playSound: true,
);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings("#drawable/notification_icon");
const InitializationSettings initializationSettings =
InitializationSettings(android: initializationSettingsAndroid);
flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onDidReceiveNotificationResponse: (details) => notificationOnClick(message),
onDidReceiveBackgroundNotificationResponse: (details) =>
firebaseMessagingBackgroundHandler,
);
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,
channelDescription: channel.description,
importance: channel.importance,
playSound: channel.playSound,
color: Colors.white),
),
payload: notification.body);
}
}
#pragma('vm:entry-point')
Future<void> localNotificationBackground(
{required NotificationResponse details}) async {
print(details.payload);
}
What I am doing wrong or what I am missing ?
I also have this function for the firebase background notification. Is this the reason ?
#pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
List<FirebaseApp> app = Firebase.apps;
if (app.length < 2) {
await Firebase.initializeApp(
name: "xxxxxxxxxx", options: DefaultFirebaseOptions.currentPlatform);
}
FirebaseMessaging messaging = FirebaseMessaging.instance;
await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: true,
criticalAlert: false,
provisional: true,
sound: true,
);
}
I want to show notification based on a condition. I tried with flutter local notification package but I was only getting the foreground and background notification. if I close the app i was not having any notification from app.
example:
app is fetching the data from real-time-database firebase and data base is getting the frequency value from hardware, if frequency is greater than 50 then show notification.
if there is any another way to implement, you can also suggest me that
part of the code:
NotificationService notificationsServices = NotificationService();
void initState(){
super.initState();
notificationsServices.intializeNotification();
}
if(_displayTemp>135 || _displayVib>135)
{
notificationsServices.sendN("Alert", _displayMsg);
}
class NotificationService {
final FlutterLocalNotificationsPlugin flutterNotificationsPlugin = FlutterLocalNotificationsPlugin();
final AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('shield');
void intializeNotification() async {
InitializationSettings initializationSettings= InitializationSettings(
android: initializationSettingsAndroid
);
await flutterNotificationsPlugin.initialize(initializationSettings);
}
void sendN(String title,String body) async {
AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails(
'channelId 2',
'channelName',
importance: Importance.max,
priority: Priority.high,
playSound: true,
//ongoing: true
);
NotificationDetails notificationDetails = NotificationDetails(
android: androidNotificationDetails,
);
await flutterNotificationsPlugin.show(
0,
title,
body,
notificationDetails
);
}
}
I use Firebase messaging in my Flutter app , I want to navigate to another screen when I click on the notification even my app is in foreground or background , I used many functions and it doesn't trigger the click event and I can't find anything can solve my problem .
When I click on the notification when app is in foreground or background , nothing happened because it navigate to the same page .
And when I click on the notification when app is terminated , it opens on Splash screen and go to the home not the screen that I want .
I added this intent-filter in my Manifest
<intent-filter>
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
And I added this to the Json object
"click_action": "FLUTTER_NOTIFICATION_CLICK",
And here is how can I get background FCM in the main.dart
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'high_importance', // id
'High Importance Notifications', // title
importance: Importance.high,
playSound: true);
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
SessionManager sessionManager = SessionManager();
await Firebase.initializeApp();
//final sound = 'sound.mp3';
print('A bg message just showed up : ${message.messageId}');
final android = AndroidInitializationSettings('#mipmap/ic_launcher');
final ios = IOSInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,);
final settings = InitializationSettings(android: android,iOS: ios);
flutterLocalNotificationsPlugin.initialize(settings,);
if(message.data['title'].toString().toLowerCase()=="new request") {
sessionManager.getBadge().then((badge) {
if (badge != null) {
int x = badge + 1;
sessionManager.saveBadge(x);
print("notification number is " + x.toString());
}
else {
sessionManager.saveBadge(1);
}
});
}
flutterLocalNotificationsPlugin.show(
message.data.hashCode,
message.data['title'],
message.data['body'],
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
importance: Importance.high,
priority: Priority.high,
// sound: RawResourceAndroidNotificationSound(sound.split('.').first),
playSound: true,
icon: '#mipmap/ic_launcher',
),
));
/*NotificationApi.showNotification(
title: message.data['title'],
body: message.data['body'],
payload: "",
id: int.parse(channel.id));*/
}
Future<void> main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
runApp(MyApps());
// configLoading();
}
class MyApps extends StatefulWidget {
const MyApps({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() {
return MyApp();
}
}
class MyApp extends State<MyApps> {
static ValueNotifier<int> strikeNotifier = ValueNotifier(0);
Color _primaryColor = Color(0xff0d8b75);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return ScreenUtilInit(
builder: () => MaterialApp(
debugShowCheckedModeBanner: false,
home: SplashScreen(),
),
designSize: const Size(1080, 2280),
);
}
void showNotification(String title, String body) async {
await _demoNotification(title, body);
}
Future<void> _demoNotification(String title, String body) async {
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'channel_I', 'channel name',
showProgress: true,
priority: Priority.high,
playSound: true,
ticker: 'test ticker');
var iOSChannelSpecifics = IOSNotificationDetails();
var platformChannelSpecifics = NotificationDetails(
android: androidPlatformChannelSpecifics, iOS: iOSChannelSpecifics);
await flutterLocalNotificationsPlugin
.show(0, title, body, platformChannelSpecifics, payload: 'test');
}
#override
void initState() {
super.initState();
FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) {
if (message != null) {
Navigator.push(context, MaterialPageRoute(builder: (context)=>DoneAndPaiedPagess(0)));
}
});
getToken().then((value) {
if(value!=null) {
AppConstants.firebaseToken = value;
}
});
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
new FlutterLocalNotificationsPlugin();
var initializationSettingsAndroid = AndroidInitializationSettings('#mipmap/ic_launcher');
var initializationSettingsIOS = IOSInitializationSettings();
var initializationSettings = InitializationSettings(android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
flutterLocalNotificationsPlugin.initialize(initializationSettings,
);
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
var data = message.data;
// AndroidNotification? android = message.notification?.android;/
if (data != null ) {
if(data['title'].toString().toLowerCase()=="new request") {
SessionManager sessionManager = SessionManager(context);
sessionManager.getBadge().then((badge) {
if (badge != null) {
setState(() {
int x = badge + 1;
strikeNotifier.value = x;
sessionManager.saveBadge(x);
});
}
else {
strikeNotifier.value = 1;
sessionManager.saveBadge(1);
}
});
}
print("entered");
flutterLocalNotificationsPlugin.show(
data.hashCode,
data['title'],
data['body'],
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
playSound: true,
icon: '#mipmap/ic_launcher',
),
));
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
Navigator.push(context, MaterialPageRoute(builder: (context)=>DoneAndPaiedPagess(0)));
});
}
Future<String?> getToken() async{
String? token = await FirebaseMessaging.instance.getToken();
print("token is "+token!);
return token;
}
}
In yaml
firebase_core: ^1.12.0
firebase_messaging: ^11.2.6
dependency_overrides:
firebase_messaging_platform_interface: 3.1.6
Edit : from multiple solutions , I tried the most common solution that I used onMessageOpenedApp in initState but it doesn't enter in it
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
Navigator.push(context, MaterialPageRoute(builder: (context)=>DoneAndPaiedPagess(0)));
});
In your code, you are using the flutter_local_notifications plugin and you are creating local notification when you get a push notification from firebase messaging.
Since the notification is not created by firebase messaging so, you are not getting on tap callback in getInitialMessage and onMessageOpenedApp.
In order to get on tap callback on local notifications, you can pass a callback function while initializing flutter_local_notifications
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
void selectNotification(String payload) async {
if (payload != null) {
debugPrint('notification payload: $payload');
// Here you can check notification payload and redirect user to the respective screen
await Navigator.push(
context,
MaterialPageRoute<void>(builder: (context) => SecondScreen(payload)),
);
}
}
For more, you can check flutter_local_notifications documentation
On your home widget within initState, check the getInitialMessage value :
// get the remote message when your app opened from push notification while in background state
RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
// check if it is exists
if (initialMessage != null) {
// check the data property within RemoteMessage and do navigate based on it
}
Check the firebase flutter documentation here https://firebase.flutter.dev/docs/messaging/notifications/#handling-interaction.
Use onMessageOpenedApp stream to listen when a notification is opened in the foreground or background. When the application is terminated, use getInitialMessage to get a pending notification.
I'm using flutterlocalnotifications package and FCM to send notifications to my users. Everything working perfectly. I can show notifications to my users when my app is in the background, foreground, or closed. I can reach payload value in foreground and background, but when the app is killed I can't reach payload anymore. I test in catlog but there isn't any print in the onSelectNotification.
How can reach this payload?
This is my notification handler class
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
AndroidNotificationChannel channel = AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
'This channel is used for important notifications.', // description
importance: Importance.high,
);
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// await Firebase.initializeApp();
//Bildirim geldiği anda datayı elde ettiğimiz alan
final dynamic data = message.data;
print('Data in background : ${data.toString()}');
NotificationHandler.showNotification(data);
}
class NotificationHandler {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
static final NotificationHandler _singleton = NotificationHandler._internal();
factory NotificationHandler() {
return _singleton;
}
NotificationHandler._internal();
BuildContext myContext;
initializeFCMNotifications(BuildContext context) async {
await _firebaseMessaging.setForegroundNotificationPresentationOptions(
alert: true, badge: true, sound: true);
var initializationSettingsAndroid =
AndroidInitializationSettings('app_icon');
var initializationSettingsIOS = IOSInitializationSettings(
onDidReceiveLocalNotification: onDidReceiveLocalNotification);
var initializationSettings = InitializationSettings(
android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
await flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
await _firebaseMessaging.subscribeToTopic("all");
await FirebaseMessaging.instance
.getInitialMessage()
.then((RemoteMessage message) {
if (message != null) {
print('initialmessage tetiklendi : ' + message.data.toString());
}
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
var notification = message.notification;
var android = message.notification?.android;
print('ONMESSAGE ÇALIŞTI');
print('onmessage tetiklendi data : ' +
message.data['message'] +
' title ' +
message.data['title']);
showNotification(message.data);
});
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('Onmessageopened : ' + message.data.toString());
});
}
static void showNotification(Map<String, dynamic> data) async {
const androidPlatformChannelSpecifics = AndroidNotificationDetails(
'1234', 'Yeni Mesaj', 'your channel description',
importance: Importance.max, priority: Priority.high, ticker: 'ticker');
const IOSPlatformChannelSpecifics = IOSNotificationDetails();
const platformChannelSpecifics = NotificationDetails(
android: androidPlatformChannelSpecifics,
iOS: IOSPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.show(
0, data['title'], data['message'], platformChannelSpecifics,
payload: 'The value when notification tapped');
}
Future onSelectNotification(String payload) async {
if (payload != null) {
debugPrint('notification payload : $payload');
}
}
Future onDidReceiveLocalNotification(
int id, String title, String body, String payload) {}
}
and these are the versions of the packages I use
firebase_messaging: ^9.1.1
flutter_local_notifications: ^5.0.0+1
If I use the show() method of the FlutterLocalNotificationsPlugin then I get a notification on Android but not on ios.
showNotification(
String notificationId, String title, String body, String payload) async {
final AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
"important-notifications",
tr("DEVICE.IMPORTANT_NOTIFICATIONS_NAME"),
tr("DEVICE.IMPORTANT_NOTIFICATIONS_DESCRIPTION"),
importance: Importance.max,
priority: Priority.high,
ledColor: Colors.pink,
ledOffMs: 50,
ledOnMs: 50,
color: Colors.purple,
styleInformation: BigTextStyleInformation(""),
ticker: 'ticker');
final NotificationDetails platformChannelSpecifics = NotificationDetails(
android: androidPlatformChannelSpecifics,
);
await flutterLocalNotificationsPlugin
.show(0, title, body, platformChannelSpecifics, payload: payload);
}
The solution to this problem is below.
There's a problem with platform specifics in the flutter_local_notifications plugin.
For example, if you use
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
.createNotificationChannel(channel);
inside of your local notification initialization function, then the function will stop at this point if you're not on an android device. This results in not initializing the plugin with ios + android settings.
Solution
To fix this just wrap everything you do with .resolvePlatformSpecificImplementation into a platform check
if (Platform.isAndroid) {
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
.createNotificationChannel(channel);
}
if (Platform.isIOS) {
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
.requestPermissions(
alert: true,
badge: true,
sound: true,
);
}