Flutter中FCM推送无法在后台/锁屏更新Live Activities问题
问题分析与解决方案
问题根源
- iOS后台/锁屏状态下,Flutter的FCM后台消息处理器
handleBackgroundMessage执行环境受限:系统会限制Flutter引擎在后台的活跃时间,甚至直接挂起进程,导致无法可靠调用Live Activities插件完成更新。 - 代码逻辑不符合iOS Live Activities的官方更新机制:后台状态下的Live Activities更新,必须通过iOS原生的推送服务扩展实现,而非依赖Flutter的后台回调。
- 全局变量
soc无法持久化:后台消息处理是独立进程,全局变量会被重置,无法实现累加更新。
核心修复方案:使用iOS推送服务扩展处理Live Activities更新
1. 添加Notification Service Extension
在Xcode中给Flutter项目新增Notification Service Extension目标(命名如LiveActivityUpdater),并确保该扩展与主App使用相同的App Group ID(即你的group.new_activity):
- 在扩展的
Info.plist中添加AppGroups权限,包含目标App Group ID。 - 确保扩展的签名配置与主App一致。
2. 编写扩展更新逻辑
替换NotificationService.swift的代码,通过扩展直接处理Live Activities更新:
import UserNotifications import ActivityKit // 定义Live Activity的属性和状态(需与Flutter端对应) struct LiveActivityAttributes: ActivityAttributes { public struct ContentState: Codable, Hashable { var soc: String } } 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) guard let content = bestAttemptContent, let socValue = content.userInfo["soc"] as? String, let activityId = UserDefaults(suiteName: "group.new_activity")?.string(forKey: "liveActivityId") else { contentHandler(content) return } // 匹配并更新目标Live Activity Task { for activity in Activity<LiveActivityAttributes>.activities { if activity.id == activityId { await activity.update(using: LiveActivityAttributes.ContentState(soc: socValue)) break } } } contentHandler(content) } override func serviceExtensionTimeWillExpire() { if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { contentHandler(bestAttemptContent) } } }
3. 调整FCM推送Payload
推送时必须携带mutable-content: 1,触发iOS调用推送服务扩展,示例Payload:
{ "to": "你的FCM设备令牌", "mutable_content": 1, "data": { "soc": "50", "activityId": "存储的Live Activity ID" }, "notification": { "title": "SOC更新", "body": "当前SOC: 50%" } }
4. 修正Flutter端代码
- 删除
handleBackgroundMessage中更新Live Activities的逻辑,后台更新不再依赖Flutter。 - 使用支持App Group的存储插件(如
shared_preferences_app_group),将Live Activity ID存入App Group的UserDefaults,确保扩展能读取:
// 创建Live Activity时存储ID到App Group final activityId = await _liveActivitiesPlugin.createActivity( LiveActivityAttributes(soc: "0"), ); final appGroupPrefs = await SharedPreferencesAppGroup().getInstance(groupId: "group.new_activity"); await appGroupPrefs.setString('liveActivityId', activityId);
5. 关键注意事项
- 确认主App和扩展都已正确配置App Group权限,且签名通过。
- Live Activities的更新推送必须包含
mutable-content: 1,否则iOS不会触发扩展。 - 禁止在Flutter后台消息回调中执行Live Activities更新操作,iOS后台对Flutter引擎的运行有严格限制,无法保证可靠性。
内容的提问来源于stack exchange,提问作者Ragul PR
相关产品推荐
相关产品推荐

