我正在为 FirebaseMessaging 制作一个 Flutter 示例应用程序,以测试服务器通知的工作方式。因此,我按照 FlutterFire 上的指南进行了所有设置,它在 Android 上运行得很好。
就 iOS 而言,当应用程序处于后台时,它似乎工作得很好。当我在前台使用应用程序发送通知时,它会显示通知两次。如果它包含图像,则第一个通知将包含该图像,而第二个通知则不会。
我很迷茫,我不明白为什么会这样做。无论我使用 Firebase 控制台的测试通知还是我自己的后端使用
FirebaseMessaging.send
,行为都是相同的。
以下是代码片段:
main.dart
import 'dart:developer';
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:googlenotifpoc/notification_manager.dart';
import 'firebase_options.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
name: "...",
options: DefaultFirebaseOptions.currentPlatform,
);
await FirebaseMessaging.instance.setAutoInitEnabled(true);
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
description:
'This channel is used for important notifications.', // description
importance: Importance.max,
);
const AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
'high_importance_channel', // id
'High Importance Notifications',
channelDescription: 'This channel is used for important notifications.',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker',
icon: "ic_white",
playSound: true,
);
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true, // Required to display a heads up notification
badge: true,
sound: true,
);
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
FirebaseMessaging.onMessage.listen((event) => NotificationManager.showNotification(event, androidDetails),);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
FirebaseMessaging messaging = FirebaseMessaging.instance;
messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: true,
criticalAlert: false,
provisional: false,
sound: true,
);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FutureBuilder<String?>(
future: getFMCToken(),
builder: (BuildContext context, AsyncSnapshot<String?> snapshot) {
if (snapshot.hasData) {
log(snapshot.data ?? "-");
print(snapshot.data ?? "-");
return Text(
"FMC TOKEN: \n ${snapshot.data}",
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 16, fontWeight: FontWeight.bold),
);
} else {
return const Text("Loading your FCM token...");
}
},
),
],
),
),
);
}
Future<String?> getFMCToken() async {
String? apnsToken;
if(Platform.isIOS) {
apnsToken = await FirebaseMessaging.instance.getAPNSToken();
log("Got APNS token: $apnsToken");
print("Got APNS token: $apnsToken");
}
if(Platform.isAndroid || apnsToken != null) {
return FirebaseMessaging.instance.getToken();
}
return Future.value(null);
}
}
通知管理类
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class NotificationManager {
static Future<void> showNotification(RemoteMessage payload, AndroidNotificationDetails androidDetails) async {
const android = AndroidInitializationSettings("ic_white");
const initializationSettingsIOS = DarwinInitializationSettings();
const initialSetting = InitializationSettings(android: android, iOS: initializationSettingsIOS);
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
flutterLocalNotificationsPlugin.initialize(initialSetting);
const iOSDetails = DarwinNotificationDetails();
NotificationDetails platformChannelSpecifics = NotificationDetails(android: androidDetails, iOS: iOSDetails);
await flutterLocalNotificationsPlugin.show(0, payload.notification!.title, payload.notification!.body, platformChannelSpecifics);
}
}
ios notificationService.m
//
// NotificationService.m
// ImageNotification
//
#import "NotificationService.h"
#import "FirebaseMessaging.h"
@interface NotificationService ()
@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
@end
@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
// Modify the notification content here...
//self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];
//self.contentHandler(self.bestAttemptContent);
[[FIRMessaging extensionHelper] populateNotificationContent:self.bestAttemptContent withContentHandler:contentHandler];
}
- (void)serviceExtensionTimeWillExpire {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
self.contentHandler(self.bestAttemptContent);
}
@end
Firebase 依赖项:
firebase_messaging: ^14.7.9
firebase_core: ^2.24.2
flutter_local_notifications: ^16.2.0
firebase_analytics: ^10.7.4
我找到了一个解决方案解决方法。看来问题来自于 firebase 插件和我的代码都尝试显示相同的通知,因此我必须将 iOS 和 Android 案例分开,以免在 iOS 上抛出本地通知:
FirebaseMessaging.onMessage.listen(
(event) {
if (Platform.isIOS) {
log("received event for iOS : $event");
return;
}
if (event.notification == null) return;
NotificationManager.showNotification(event, androidDetails);
},
);