Flutter中iOS后台/终止状态下App图标角标计数不更新的解决办法
iOS后台/终止状态下FCM通知角标不更新的解决方案
问题根源
iOS在后台或终止状态时,Flutter的onBackgroundMessage仅在通知为静默通知(携带content_available: true)时才会触发;普通通知由系统直接展示,不会执行Flutter层的角标更新代码。同时iOS的角标计数由系统管理,无法通过Flutter后台代码直接修改。
方案1:直接通过FCM通知Payload设置角标
这是最直接的解决方案,利用iOS系统原生支持,在发送FCM通知时携带badge字段,系统会自动更新角标。
后端发送Payload示例
{ "notification": { "title": "新通知", "body": "通知内容" }, "apns": { "payload": { "aps": { "badge": "+1", // 表示在当前角标基础上递增1;也可以设具体数字(如3) "content-available": 1 // 可选,若需要触发Flutter后台handler则添加 } } }, "token": "目标设备FCM Token" }
注意事项
- 使用
"+1"增量时,需确保本地存储的计数与系统角标保持一致,避免计数混乱。 - 若要清零角标,直接将
badge设为0即可。
方案2:同步本地计数与系统角标
如果需要本地维护通知计数(比如用户点击通知后清零),需在App启动或激活时同步数据:
1. 点击通知时重置角标
在getInitialMessage和onMessageOpenedApp回调中添加清零逻辑:
// 处理App终止状态下点击通知 FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) async { if (message != null) { LocalNotificationService.display(message); // 重置角标 if (await FlutterAppBadger.isAppBadgeSupported()) { await FlutterAppBadger.updateBadgeCount(0); await UserSecureStorage.setField("notificationBadgeCount", "0"); } } }); // 处理后台状态下点击通知 FirebaseMessaging.onMessageOpenedApp.listen((message) async { LocalNotificationService.display(message); if (await FlutterAppBadger.isAppBadgeSupported()) { await FlutterAppBadger.updateBadgeCount(0); await UserSecureStorage.setField("notificationBadgeCount", "0"); } });
2. App回到前台时同步计数
监听App生命周期,在前台时同步本地计数到系统角标:
// 注册生命周期监听 WidgetsBinding.instance.addObserver(LifecycleEventHandler( resumeCallBack: () async { if (await FlutterAppBadger.isAppBadgeSupported()) { final countCache = await UserSecureStorage.getField("notificationBadgeCount"); final int count = int.parse(countCache ?? "0"); await FlutterAppBadger.updateBadgeCount(count); } }, )); // 自定义生命周期处理类 class LifecycleEventHandler extends WidgetsBindingObserver { final AsyncCallback? resumeCallBack; LifecycleEventHandler({this.resumeCallBack}); @override Future<void> didChangeAppLifecycleState(AppLifecycleState state) async { if (state == AppLifecycleState.resumed) { await resumeCallBack?.call(); } } }
方案3:使用iOS原生通知扩展(动态计算角标)
如果需要根据未读通知数动态计算角标(比如从API获取实时计数),可以创建iOS原生的Notification Service Extension,在扩展中修改通知的badge字段(App终止状态下也会被系统调用)。
1. 创建通知扩展
在Xcode中打开iOS项目,依次选择:File > New > Target > Notification Service Extension,完成创建。
2. 修改扩展代码
在NotificationService.swift中实现计数逻辑:
import UserNotifications class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) if let bestAttemptContent = bestAttemptContent { // 通过共享组获取本地存储的计数(需提前配置App Groups) let userDefaults = UserDefaults(suiteName: "group.your.app.bundle.id") let currentCount = userDefaults?.integer(forKey: "notificationBadgeCount") ?? 0 let newCount = currentCount + 1 // 更新共享存储和角标 userDefaults?.set(newCount, forKey: "notificationBadgeCount") userDefaults?.synchronize() bestAttemptContent.badge = NSNumber(value: newCount) contentHandler(bestAttemptContent) } } override func serviceExtensionTimeWillExpire() { // 超时 fallback if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { contentHandler(bestAttemptContent) } } }
3. Flutter端同步数据
使用支持App Groups的存储插件(如shared_preferences),与扩展共享计数数据:
// 初始化时指定共享组 SharedPreferences.setPrefix('group.your.app.bundle.id'); final prefs = await SharedPreferences.getInstance(); // 读写计数逻辑与原代码一致
必要配置检查
- 确保iOS项目
Info.plist开启远程通知后台模式:
<key>UIBackgroundModes</key> <array> <string>remote-notification</string> </array>
- 请求通知权限时包含
badge权限:
await FirebaseMessaging.instance.requestPermission( badge: true, alert: true, sound: true, );
内容的提问来源于stack exchange,提问作者Ugurcan Ucar
相关产品推荐
相关产品推荐

