I use firebase messaging for chats on a Flutter app. I added configuration part for firebase and notification, but when the user click on notification, the screen not opened and the FirebaseMessaging.onMessageOpenedApp method doesn't fired
I added firebase configuration on main method
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
await SessionManagement.init();
await SessionManagement.setUser();
DioUtil.getInstance();
if (!kIsWeb) {
channel = const AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
description: 'This channel is used for important notifications.',
importance: Importance.high,
);
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
await firebaseMessaging
.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
}
runApp(AppProvider(child: MyApp()));
}
AndroidNotificationChannel channel;
FirebaseMessaging firebaseMessaging = FirebaseMessaging.instance;
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
if(kDebugMode)
print('Handling a background message ${message.data}');
}
Then I added notification configuration on main screen.
#override
void initState() {
super.initState();
initFirebaseConfig();
}
void initFirebaseConfig() async {
requestPermission();
FirebaseMessaging.instance
.getInitialMessage()
.then((RemoteMessage message) {
if (message != null) {
_handleMessage(message);
}
});
FirebaseMessaging.onMessage.listen(showFlutterNotification);
FirebaseMessaging.onMessageOpenedApp.listen((event) {
print('Just received a notification when app is opened');
_handleMessage(event);
});
}
void showFlutterNotification(RemoteMessage message) {
print("heeeey onMessage" + message.toString());
RemoteNotification notification = message.notification;
AndroidNotification android = message.notification?.android;
if (notification != null && android != null && !kIsWeb) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
"New message from ${notification.title}",
notification.body,
NotificationDetails(
iOS: IOSNotificationDetails(subtitle: notification.title),
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
icon: 'ic_notifications_icon',
),
),
);
}
}
void _handleMessage(RemoteMessage message) async {
print("heeeey onMessageOpenedApp" + message.toString());
NotificationModel notificationData =
NotificationModel.fromJson(message.data);
print(notificationData.data.toString());
if (notificationData.screen == "chats") {
Chat chat = Chat.fromJson(data: jsonDecode(notificationData.data));
print(chat.toString());
Navigator.of(context, rootNavigator: true)
.pushNamed(ChatScreen.routeName, arguments: chat);
// MyApp.navigatorKey.currentState.pushNamed('contact');
}
}
void requestPermission() async {
try {
NotificationSettings settings = await firebaseMessaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
print('User granted permission: ${settings.authorizationStatus}');
if (Platform.isIOS) {
await firebaseMessaging.setForegroundNotificationPresentationOptions(
alert: true, // Required to display a heads up notification
badge: true,
sound: true,
);
}
} catch (error) {
print('permission rejected');
}
}
After sending message I request firebase send message
static Future<void> sendNotification(
Chat chat, String deviceToken, String title) async {
final postUrl = 'https://fcm.googleapis.com/fcm/send';
NotificationModel notificationData = NotificationModel(
id: chat.id,
clickAction: "FLUTTER_NOTIFICATION_CLICK",
screen: "chats",
data: jsonEncode(chat.toJson()));
final data = {
"notification": {"body": chat.lastMessage.body, "title": title},
"priority": "high",
"data": notificationData.toJson(),
"to": deviceToken,
};
final headers = {
'content-type': 'application/json',
'Authorization':
'key=FIREBASE_KEY',
};
final response = await http.post(Uri.parse(postUrl),
body: json.encode(data),
encoding: Encoding.getByName('utf-8'),
headers: headers);
if (response.statusCode == 200) {
if (kDebugMode) print("true");
} else {
if (kDebugMode) print("heeeey!" + response.statusCode.toString());
}
}
I tried many code but it's still not working, could you please help me to find the issue
Related
I have a app with firebase messaging.
When app is open, I received a remote notification and show a local notification.
When app is killed, and I send a push notification and click, not redirect to specific url, but if app is opened or foreground yes!
My code:
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print('Handling a background message ${message.messageId}');
flutterLocalNotificationsPlugin.show(
message.data.hashCode,
message.data['title'],
message.data['body'],
NotificationDetails(
android: AndroidNotificationDetails(channel.id, channel.name,
channelDescription: channel.description),
),
payload: message.data['click_action']);
}
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
description:
'This channel is used for important notifications.', // description
importance: Importance.high,
);
const IOSNotificationDetails iosNotificationDetails = IOSNotificationDetails();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await Permission.notification.request();
await FirebaseMessaging.instance.subscribeToTopic("NEWS");
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
print('User granted permission');
} else if (settings.authorizationStatus == AuthorizationStatus.provisional) {
print('User granted provisional permission');
} else {
print('User declined or has not accepted permission');
}
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
runApp(App());
}
app.dart
Future<void> getToken() async {
final FirebaseMessaging _fcm = FirebaseMessaging.instance;
final token = await _fcm.getToken();
print("#####################################################");
print(token.toString());
print("#####################################################");
}
class _AppState extends State<App> {
#override
void initState() {
var initialzationSettingsAndroid =
AndroidInitializationSettings('#mipmap/ic_launcher');
var initializationSettings = InitializationSettings(
android: initialzationSettingsAndroid,
iOS: IOSInitializationSettings(),
);
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
AppleNotification? ios = message.notification?.apple;
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
icon: android.smallIcon,
),
iOS: IOSNotificationDetails()),
payload: message.data['click_action']);
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
String? ask = message.data['click_action'];
if (ask!.contains("www")) {
openLink(Uri.parse(message.data['click_action']));
} else if (ask == "APP") {
//GO TO PAGE
} else {
null;
}
});
getToken();
super.initState();
}
Future onSelectNotification(String? payload) async {
String? ask = payload;
if (ask!.contains("www")) {
openLink(Uri.parse(payload!));
} else if (ask == "APP") {
//GO TO PAGE
} else {
null;
}
}
#override
Widget build(BuildContext context) {..}
}
void openLink(_url) async {
if (!await launchUrl(
_url,
mode: LaunchMode.externalApplication,
)) throw 'Could not launch $_url';
}
I send the notification with firebase console:
{
"to" : "/topics/NEWS",
"priority": "high",
"importance":"max",
"notification": {
"title": "title",
"body" : "body",
"text": "Text",
"sound":"default"
},
"data":{
"click_action":"https://www.google.com"
}
}
I am actually working with a e-commerce app and want to send notification to a delivery boy when order is placed from user app.
How can we identify the specific delivery boy app from backgroud (when app is exited).
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage
message) async {
await Firebase.initializeApp();
SharedPreferences _prefs = await SharedPreferences.getInstance();
print('A message just showed : ${message.messageId}');
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// <------------Local Notification Initilization-------------->
FirebaseMessaging.onBackgroundMessage(
_firebaseMessagingBackgroundHandler);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
runApp(MyApp());
}
Use http request to send notifications, and use topic parameter for specific user selection, check the code example that I've used in my case:
final String serverToken = 'YOUR_SERVER_TOKEN_HERE';
FirebaseMessaging firebaseMessaging = FirebaseMessaging.instance;
Future<Map<String, dynamic>> sendAndRetrieveMessage(String typeOfNotification,
{String? sellerId, String? chatId, String? postId}) async {
NotificationSettings settings = await firebaseMessaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
String notification = getNotificationMessage(typeOfNotification);
notificationMsg.value = notification;
addToNotifications(sellerId!, notification, postId ?? "$userId", chatId ?? "$userId");
print('User granted permission: ${settings.authorizationStatus}');
String topic = '';
if (typeOfNotification == 'live') {
topic = "/topics/$userId";
} else if (typeOfNotification == 'post') {
topic = "/topics/$userId";
} else {
topic = "/topics/${sellerId}_personal";
}
await http.post(
Uri.parse('https://fcm.googleapis.com/fcm/send'),
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization': 'key=$serverToken',
},
body: jsonEncode(
<String, dynamic>{
'notification': <String, dynamic>{
'body': "${userIsASeller.value ? shopName.value : userName.value} $notification",
'title': "Shopenlive",
},
'priority': 'high',
'data': <String, dynamic>{
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
'id': '1',
'status': 'done',
'title': "Shopenlive",
'body': "${userIsASeller.value ? shopName.value : userName.value} $notification",
},
'to': topic,
},
),
);
final Completer<Map<String, dynamic>> completer = Completer<Map<String, dynamic>>();
/* firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
completer.complete(message);
},
); */
return completer.future;
}
I am trying to redirect user to specific page in flutter web on click of notification, till now I am receiving notification, the last open page is displayed in browser on click of notification, but I want user to redirect to specific page like specific post using unique post id, but I found no other ways, This thing happens in Android build, but doesn't happen on web build.
just take a look at ServiceWorker.js
in serviceworker.js even console.log() in not executing on receiving notification.
messaging.setBackgroundMessageHandler(function (payload) {
console.log(payload);
const promiseChain = clients
.matchAll({
type: "window",
includeUncontrolled: true
})
.then(windowClients => {
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
windowClient.postMessage(payload);
}
})
.then(() => {
const title = payload.notification.title;
const options = {
body: payload.notification.body
};
return registration.showNotification(title, options);
});
return promiseChain;
});
self.addEventListener('notificationclick', function (event) {
console.log('notification received: ', event)
});
now the logic for sending FCM Push Notification:
#required String message,
#required String fcmToken,
#required String title,
String type,
String typeId,
}) async {
await http
.post(
Uri.parse('https://fcm.googleapis.com/fcm/send'),
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization': 'key=$serverToken',
},
body: jsonEncode(
<String, dynamic>{
'notification': <String, dynamic>{
"click_action": 'FLUTTER_NOTIFICATION_CLICK',
'body': message,
'title': '$title',
'sound': 'default'
},
'priority': 'high',
'data': <String, dynamic>{
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
'id': '1',
'status': 'done',
'type': type ?? "",
'typeId': typeId ?? "",
},
'to': fcmToken.toString().trim(),
},
),
)
.then((value) {
print("Notification Sent");
});
final Completer<Map<String, dynamic>> completer =
Completer<Map<String, dynamic>>();
// firebaseMessaging.configure(
// onMessage: (Map<String, dynamic> message) async {
// completer.complete(message);
// },
// );
return completer.future;
}
Now the way I am handling Notification Receive:
print("in setup interacted message");
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
'This channel is used for important notifications.', // description
importance: Importance.max,
);
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
NotificationSettings notifSettings =
await FirebaseMessaging.instance.requestPermission(
alert: true,
announcement: true,
badge: true,
carPlay: true,
criticalAlert: true,
provisional: true,
sound: true,
);
if (notifSettings.authorizationStatus == AuthorizationStatus.authorized) {
// Get any messages which caused the application to open from
// a terminated state.
try {
RemoteMessage initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
// If the message also contains a data property with a "type" of "post",
// navigate to a post screen
if (initialMessage != null && initialMessage.data['type'] == 'post') {
await Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) => IndividualPostPage(
postObjectId: initialMessage.data['typeId'],
),
),
);
print(initialMessage.data);
initialMessage = null;
}
// Also handle any interaction when the app is in the background via a
// Stream listener
FirebaseMessaging.onMessageOpenedApp
.listen((RemoteMessage message) async {
print("here");
if (message != null && message.data['type'] == 'post') {
if (message.data['type'] == 'post') {
await Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) => IndividualPostPage(
postObjectId: message.data['typeId'],
),
),
);
}
print(message.data);
message = null;
}
});
} catch (e) {
print("Error $e");
}
}
}
messaging.setBackgroundMessageHandler(function (payload) {
console.log(payload);
const promiseChain = clients
.matchAll({
type: "window",
includeUncontrolled: true
})
.then(windowClients => {
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
windowClient.postMessage(payload);
}
})
.then(() => {
const title = payload.notification.title;
var click_action =payload.data.ui_route;//ui route is ur route
const options = {
body: payload.notification.body ,
data: {
click_action,
}
};
return registration.showNotification(title, options);
});
return promiseChain;
});
// Notification click event listener
self.addEventListener('notificationclick', e => {
data=e.notification.data.obj;
// Close the notification popout
e.notification.close();
// Get all the Window clients
e.waitUntil(clients.matchAll({ type: 'window' }).then(clientsArr => {
// If a Window tab matching the targeted URL already exists, focus that;
const hadWindowToFocus = clientsArr.some(windowClient => windowClient.url === e.notification.data.click_action ? (windowClient.focus(), true) : false);
// Otherwise, open a new tab to the applicable URL and focus it.
if (!hadWindowToFocus) clients.openWindow(e.notification.data.click_action).then(windowClient => windowClient ? windowClient.focus() : null);
}));
});
I got this error log when I build app on a Emulator.
even the app start regularly this Fatal Error appears right when main.dart is executed:
E/FlutterFcmService( 6350): Fatal: failed to find callback
the question is that no notifications is executed because this error appears right when I start the app without pushing any notifications.
any input?
what I use:
firebase_messaging: ^7.0.3
flutter_local_notifications: ^4.0.1
this is the code which handle the notifications, but the error is before it will executed:
final FirebaseMessaging firebaseMessaging = FirebaseMessaging();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
#override
void initState() {
super.initState();
registerNotification();
configLocalNotification();
}
void registerNotification() {
firebaseMessaging.requestNotificationPermissions();
firebaseMessaging.configure(onMessage: (Map<String, dynamic> message) {
print('onMessage: $message');
Platform.isAndroid
? showNotification(message['notification'])
: showNotification(message['aps']['alert']);
return ;
}, onResume: (Map<String, dynamic> message) {
print('onResume: $message');
return Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NotificationsScreen()));
}, onLaunch: (Map<String, dynamic> message) {
print('onLaunch: $message');
return;
},
onBackgroundMessage: myBackgroundMessageHandler
);
firebaseMessaging.getToken().then((token) {
print('token: $token');
FirebaseFirestore.instance
.collection('Consultant')
.doc(firebaseUser.uid)
.update({'deviceToken': token});
}).catchError((err) {
//Fluttertoast.showToast(msg: err.message.toString());
});
}
Future selectNotification(String payload) async {
if (payload != null) {
debugPrint('notification payload: $payload');
}
await Navigator.push(
context,
MaterialPageRoute<void>(builder: (context) => NotificationsScreen(payload: payload,)),
);
}
void showNotification(message) async {
var androidPlatformChannelSpecifics = new AndroidNotificationDetails(
Platform.isAndroid
? 'it.wytex.vibeland_pro_app'
: 'it.wytex.vibeland_pro_app',
'Vibeland Pro',
'Vibeland Pro',
playSound: true,
enableVibration: true,
importance: Importance.max,
priority: Priority.high,
icon: '#drawable/ic_stat_vibeland_pro'
);
var iOSPlatformChannelSpecifics = new IOSNotificationDetails();
var platformChannelSpecifics = new NotificationDetails(
android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics);
print(message);
print(message['body'].toString());
print(json.encode(message));
// await flutterLocalNotificationsPlugin.show(2, message['title'].toString(),
// message['body'].toString(), platformChannelSpecifics,
// payload: json.encode(message));
await flutterLocalNotificationsPlugin.show(
3, '📩 Hai ricevuto un messaggio 📩 ', 'Controlla subito le Tue notifiche 🔔🔔', platformChannelSpecifics,
payload: '#drawable/ic_stat_vibeland_pro',
);
}
void configLocalNotification() {
var initializationSettingsAndroid =
new AndroidInitializationSettings('#drawable/ic_stat_vibeland_pro');
var initializationSettingsIOS = new IOSInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
);
var initializationSettings = new InitializationSettings(
android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
flutterLocalNotificationsPlugin.initialize(initializationSettings);
}
and background messagge are handle externally of the class:
Future<void> myBackgroundMessageHandler(message) async {
print("Handling a background message: ${message}");
}
this below the function added to Firebase
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
const fcm = admin.messaging();
exports.sendNotification = functions.firestore
.document("Notifications/{id}")
.onCreate((snapshot) => {
const name = snapshot.get("name");
const subject = snapshot.get("subject");
const token = snapshot.get("token");
const payload = {
notification: {
title: "" + name,
body: "" + subject,
sound: "default",
icon: "#drawable/ic_stat_vibeland_pro",
},
data: {
click_action: "FLUTTER_NOTIFICATION_CLICK",
},
};
return fcm.sendToDevice(token, payload);
});
not the error I get is before we call these functions...
I am updating my Flutter app in order to receive Firebase Cloud Messaging messages.
I have followed all needed changes in the app and on Firebase console.
Now I am trying to send a test message from FCM to my android device, but it only receives a message when sent using the test message option with an specific token.
It doesn't receive a normal message to all devices.
This is the main.dart code I am using:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
},
);
_firebaseMessaging.requestNotificationPermissions(
const IosNotificationSettings(
sound: true, badge: true, alert: true, provisional: false));
_firebaseMessaging.onIosSettingsRegistered
.listen((IosNotificationSettings settings) {
print("Settings registered: $settings");
});
_firebaseMessaging.getToken().then((String token) {
assert(token != null);
print("token firebase " + token);
});
runApp(new MaterialApp(
home: new MyApp(),
));
What am I doing wrong?
Make sure that all devices are subscribed to topic.