我在弄清楚如何通过 Flutter 上的 firebase 推送通知达到我想要的结果时遇到了一些麻烦。
基本上,我尝试在设备离线的情况下发送一组推送通知,然后我重新激活 wifi,但设备只收到一个通知。
我已经阅读了this,但我不知道如何接收所有通知并将其显示给用户。 基本上和whatsapp 一样。 文字
如果在通知消息中我设置了
notification
(带或不带data
)键,则设备重新上线后只会收到一个通知。
如果在通知消息中我设置了 data
(没有 notification
),则无法设置标题和正文,因此不会向用户显示通知。
这是我用来处理 firebase 通知的代码:
import 'dart:convert';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
Future handleBackgroundMessage(RemoteMessage message) async {
print("Title: ${message.notification?.title}");
print("Body: ${message.notification?.body}");
print("Payload: ${message.data}");
}
class FirebaseApi {
final _firebaseMessaging = FirebaseMessaging.instance;
final _androidChannel = const AndroidNotificationChannel(
'high_importance_channel',
'High Importance Notifications',
description: "This channel is used for important notifications",
importance: Importance.defaultImportance,
);
final _localNotification = FlutterLocalNotificationsPlugin();
void handleMessage(RemoteMessage? message) {
if (message == null) return;
print("Title: ${message.notification?.title}");
print("Body: ${message.notification?.body}");
print("Payload: ${message.data}");
//action after the user tapped on the notification
}
Future initLocalNotifications() async {
const iOS = DarwinInitializationSettings();
const android = AndroidInitializationSettings("@drawable/ic_launcher");
const settings = InitializationSettings(android: android, iOS: iOS);
await _localNotification.initialize(settings,
onDidReceiveNotificationResponse: (response) {
final message = RemoteMessage.fromMap(jsonDecode(response.payload!));
handleMessage(message);
});
final platform = _localNotification.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>();
await platform?.createNotificationChannel(_androidChannel);
}
Future initPushNotifications() async {
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true, badge: true, sound: true);
FirebaseMessaging.instance.getInitialMessage().then(handleMessage);
FirebaseMessaging.onMessageOpenedApp.listen(handleMessage);
FirebaseMessaging.onBackgroundMessage(handleBackgroundMessage);
FirebaseMessaging.onMessage.listen(
(message) {
final notification = message.notification;
_localNotification.show(
notification.hashCode,
notification?.title ?? "Default title",
notification?.body ?? "Default body",
NotificationDetails(
android: AndroidNotificationDetails(
_androidChannel.id, _androidChannel.name,
channelDescription: _androidChannel.description,
icon: "@drawable/ic_launcher"),
),
payload: jsonEncode(
message.toMap(),
),
);
},
);
}
Future<String?> initNotification() async {
await _firebaseMessaging.requestPermission();
String? fcmToken = await _firebaseMessaging.getToken();
//log
print(fcmToken);
//start notifications
initPushNotifications();
initLocalNotifications();
return fcmToken;
}
}
任何帮助将不胜感激。
经过大量测试,我找到了一种解决方法,不知道这是否是最好的解决方案,但它工作正常。
我暂时只在 Android 上测试过。
基本上,我从我的 php 服务器将此负载发送到 Firebase V1 API:
{
"message":{
"token":"YOUR TOKEN",
"data":{
"title":"Title",
"body":"Body"
},
"android":{
"priority":"high"
},
"apns":{
"headers":{
"apns-priority":"10"
}
}
}
}
通过省略
notification
标签,因为 firebase 文档显示“发送静默消息”并且通知不会出现在设备上,因此需要手动通知,在我的代码中我使用 flutter_local_notification 包。
@pragma('vm:entry-point')
Future handleBackgroundMessage(RemoteMessage message) async {
/*print("background");
print(message.toMap());*/
try {
FirebaseApi firebaseApi = FirebaseApi();
await firebaseApi.initAndSendLocalNotification(message);
} catch (e) {
print(e);
}
}
class FirebaseApi {
final _androidChannel = const AndroidNotificationChannel(
'high_importance_channel',
'High Importance Notifications',
description: "This channel is used for important notifications",
importance: Importance.max,
);
final _localNotification = FlutterLocalNotificationsPlugin();
void handleMessage(RemoteMessage? message) async {
/*print("handle message");
print(message?.toMap());*/
if (message == null) return;
//YOUR ACTION HERE
}
void handleInitialMessage(RemoteMessage? message) async {
/*print("initial message");
print(message?.toMap());*/
if (message == null) return;
//YOUR ACTION HERE
}
Future initLocalNotifications() async {
const iOS = DarwinInitializationSettings();
const android = AndroidInitializationSettings("@drawable/ic_launcher");
const settings = InitializationSettings(android: android, iOS: iOS);
await _localNotification.initialize(settings,
onDidReceiveNotificationResponse: (response) {
final message = RemoteMessage.fromMap(jsonDecode(response.payload!));
handleMessage(message);
});
final appLaunchDetails =
await _localNotification.getNotificationAppLaunchDetails();
if (appLaunchDetails != null && appLaunchDetails.didNotificationLaunchApp) {
if (appLaunchDetails.notificationResponse != null) {
final message = RemoteMessage.fromMap(
jsonDecode(appLaunchDetails.notificationResponse!.payload!));
handleInitialMessage(message);
}
}
final platform = _localNotification.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>();
await platform?.createNotificationChannel(_androidChannel);
}
initAndSendLocalNotification(RemoteMessage message) async {
await initLocalNotifications();
sendLocalNotification(message);
}
sendLocalNotification(RemoteMessage message) {
final notification = message.data;
_localNotification.show(
message.hashCode,
notification["title"],
notification["body"],
NotificationDetails(
android: AndroidNotificationDetails(
_androidChannel.id, _androidChannel.name,
channelDescription: _androidChannel.description,
icon: "@drawable/ic_launcher"),
),
payload: jsonEncode(
message.toMap(),
),
);
}
Future initPushNotifications() async {
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true, badge: true, sound: true);
//FirebaseMessaging.instance.getInitialMessage().then(handleInitialMessage);
//FirebaseMessaging.onMessageOpenedApp.listen(handleBackgroundMessage);
FirebaseMessaging.onBackgroundMessage(handleBackgroundMessage);
FirebaseMessaging.onMessage.listen(
(message) {
//print(message.toMap());
sendLocalNotification(message);
},
);
}
Future<String?> initNotification() async {
final firebaseMessaging = FirebaseMessaging.instance;
await firebaseMessaging.requestPermission();
String? fcmToken = await firebaseMessaging.getToken();
//start notifications
initPushNotifications();
initLocalNotifications();
return fcmToken;
}
}
通过这样做,前台、后台、终止状态应用程序通知都会得到正确处理,即使设备在离线时收到通知,一旦在线,所有通知都将发送到设备,因为 firebase 不会折叠待处理的通知。
希望它对某人有用。