Flutter中如何将Firebase RemoteMessage编码为JSON存储
方案可行性判断
你的实现思路完全正确。
如果只是存储少量推送消息(比如最新一条推送、和未读计数关联的消息),用SharedPreferences存储序列化后的JSON字符串是成本最低、实现最简便的方案,不需要引入额外本地数据库依赖。只有当你需要存储大量历史推送、需要做消息查询筛选时,才需要考虑换成Hive、SQFlite这类本地持久化方案。
你现有代码的核心问题是:直接将RemoteNotification类型的notification字段放入序列化Map,该类型无法直接被jsonEncode处理,会导致序列化失败。
完整可复用的序列化/反序列化扩展
以下是补全的全字段序列化逻辑,同时配套了反序列化方法,存完之后可以直接从SharedPreferences读取出JSON字符串转回RemoteMessage对象使用:
extension RemoteMessageJsonExtension on RemoteMessage { // 序列化:RemoteMessage转可Json编码的Map Map<String, dynamic> toJson() { return { 'senderId': senderId, 'category': category, 'collapseKey': collapseKey, 'contentAvailable': contentAvailable, 'data': data, 'from': from, 'messageId': messageId, 'messageType': messageType, 'mutableContent': mutableContent, // 递归序列化notification字段 'notification': notification?.toJson(), 'sentTime': sentTime?.millisecondsSinceEpoch, 'threadId': threadId, 'ttl': ttl, }; } // 反序列化:Map转RemoteMessage对象 static RemoteMessage fromJson(Map<String, dynamic> json) { return RemoteMessage( senderId: json['senderId'], category: json['category'], collapseKey: json['collapseKey'], contentAvailable: json['contentAvailable'], data: Map<String, String>.from(json['data'] ?? {}), from: json['from'], messageId: json['messageId'], messageType: json['messageType'], mutableContent: json['mutableContent'], notification: json['notification'] != null ? RemoteNotificationExtension.fromJson(json['notification']) : null, sentTime: json['sentTime'] != null ? DateTime.fromMillisecondsSinceEpoch(json['sentTime']) : null, threadId: json['threadId'], ttl: json['ttl'], ); } } // RemoteNotification配套序列化扩展 extension RemoteNotificationExtension on RemoteNotification { Map<String, dynamic> toJson() { return { 'title': title, 'body': body, 'android': android?.toJson(), 'apple': apple?.toJson(), 'web': web?.toJson(), }; } static RemoteNotification fromJson(Map<String, dynamic> json) { return RemoteNotification( title: json['title'], body: json['body'], android: json['android'] != null ? AndroidNotificationExtension.fromJson(json['android']) : null, apple: json['apple'] != null ? AppleNotificationExtension.fromJson(json['apple']) : null, web: json['web'] != null ? WebNotificationExtension.fromJson(json['web']) : null, ); } } // Android平台通知字段序列化(不需要平台专属字段可删除) extension AndroidNotificationExtension on AndroidNotification { Map<String, dynamic> toJson() { return { 'channelId': channelId, 'clickAction': clickAction, 'color': color, 'count': count, 'imageUrl': imageUrl, 'link': link, 'priority': priority, 'smallIcon': smallIcon, 'sound': sound, 'ticker': ticker, 'visibility': visibility, 'tag': tag, }; } static AndroidNotification fromJson(Map<String, dynamic> json) { return AndroidNotification( channelId: json['channelId'], clickAction: json['clickAction'], color: json['color'], count: json['count'], imageUrl: json['imageUrl'], link: json['link'], priority: json['priority'] ?? AndroidNotificationPriority.defaultPriority, smallIcon: json['smallIcon'], sound: json['sound'], ticker: json['ticker'], visibility: json['visibility'] ?? AndroidNotificationVisibility.private, tag: json['tag'], ); } } // Apple平台通知字段序列化(不需要平台专属字段可删除) extension AppleNotificationExtension on AppleNotification { Map<String, dynamic> toJson() { return { 'badge': badge, 'sound': sound?.toJson(), 'imageUrl': imageUrl, 'subtitle': subtitle, 'subtitleLocArgs': subtitleLocArgs, 'subtitleLocKey': subtitleLocKey, 'threadIdentifier': threadIdentifier, }; } static AppleNotification fromJson(Map<String, dynamic> json) { return AppleNotification( badge: json['badge'], sound: json['sound'] != null ? AppleNotificationSoundExtension.fromJson(json['sound']) : null, imageUrl: json['imageUrl'], subtitle: json['subtitle'], subtitleLocArgs: List<String>.from(json['subtitleLocArgs'] ?? []), subtitleLocKey: json['subtitleLocKey'], threadIdentifier: json['threadIdentifier'], ); } } extension AppleNotificationSoundExtension on AppleNotificationSound { Map<String, dynamic> toJson() { return { 'critical': critical, 'name': name, 'volume': volume, }; } static AppleNotificationSound fromJson(Map<String, dynamic> json) { return AppleNotificationSound( critical: json['critical'] ?? false, name: json['name'], volume: json['volume'] ?? 1, ); } } // Web平台通知字段序列化(不需要平台专属字段可删除) extension WebNotificationExtension on WebNotification { Map<String, dynamic> toJson() { return { 'analyticsLabel': analyticsLabel, 'image': image, 'link': link, }; } static WebNotification fromJson(Map<String, dynamic> json) { return WebNotification( analyticsLabel: json['analyticsLabel'], image: json['image'], link: json['link'], ); } }
使用示例
存储消息:
saveMessage(RemoteMessage message) async { const String tag = "latest_firebase_message"; final SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setString(tag, jsonEncode(message.toJson())); }
读取消息:
Future<RemoteMessage?> getSavedMessage() async { const String tag = "latest_firebase_message"; final SharedPreferences prefs = await SharedPreferences.getInstance(); final String? messageStr = prefs.getString(tag); if (messageStr == null) return null; return RemoteMessageJsonExtension.fromJson(jsonDecode(messageStr)); }
如果你不需要保留各平台专属的通知细节字段,可以删掉对应平台的扩展代码,只在
RemoteNotification的toJson方法里保留title、body两个核心展示字段即可,代码量会大幅精简。
内容的提问来源于stack exchange,提问作者Sajjad
相关产品推荐
相关产品推荐

