You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
// 读写计数逻辑与原代码一致

必要配置检查

  1. 确保iOS项目Info.plist开启远程通知后台模式:
<key>UIBackgroundModes</key>
<array>
  <string>remote-notification</string>
</array>
  1. 请求通知权限时包含badge权限:
await FirebaseMessaging.instance.requestPermission(
  badge: true,
  alert: true,
  sound: true,
);

内容的提问来源于stack exchange,提问作者Ugurcan Ucar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 10:12:45