You need to enable JavaScript to run this app. Flutter 基于SharedPreferences实现FCM通知持久化存储与展示
优惠活动
大模型
产品
解决方案
定价
更多

Flutter 基于SharedPreferences实现FCM通知持久化存储与展示

通知数据持久化实现方案

现有代码的核心问题

  • 存储逻辑类型不匹配:SharedPreferences的setStringList要求传入List<String>类型,原代码把notifList.toString()(单个字符串)直接传入,不仅无法正常写入,还会触发类型报错
  • 存储时机完全错误:在initState阶段调用存储方法时,notifList还是初始空列表,此时写入会直接覆盖掉本地已保存的历史通知数据
  • 缺少数据读取流程:页面初始化时没有从本地读取历史通知的逻辑,notifList永远是空的,就算存了数据也渲染不出来
  • 复杂结构未做序列化:SharedPreferences不支持直接存Map、嵌套列表这类复杂结构,直接toString转出来的字符串无法反序列化回原数据结构

具体实现步骤

  1. 引入序列化依赖:用Dart自带的dart:convert库处理JSON转换,不需要额外添加第三方依赖
  2. 实现统一的存储、读取方法
    把通知列表统一序列化成JSON字符串存入SharedPreferences,读取时再反序列化回Map列表结构,代码如下:
    import 'dart:convert';
    
    // 从本地读取历史通知列表
    Future<void> _loadSavedNotifications() async {
      final sharePref = await SharedPreferences.getInstance();
      final savedJson = sharePref.getString('push_notifications') ?? '';
      
      if (savedJson.isEmpty) {
        setState(() => notifList = []);
        return;
      }
    
      final List<dynamic> decodedList = jsonDecode(savedJson);
      setState(() {
        notifList = decodedList
            .map((item) => Map<String, String?>.from(item as Map))
            .toList();
      });
    }
    
    // 把当前通知列表持久化到本地
    Future<void> _saveNotifications() async {
      final sharePref = await SharedPreferences.getInstance();
      // 兼容空安全,把原Map转成可序列化的Map结构
      final serializedList = notifList
          .map((map) => Map<String, String?>.from(map))
          .toList();
      final jsonStr = jsonEncode(serializedList);
      // 直接以JSON字符串形式存储,不需要用setStringList
      await sharePref.setString('push_notifications', jsonStr);
    }
    
  3. 修正FCM消息监听的触发逻辑
    不管是前台收到通知、冷启动点通知进入、后台点通知进入,只要把新通知加入notifList,就立刻触发存储方法,避免内存数据丢失:
    Future<dynamic> fcmMessageReceiver() async {
      // 冷启动打开app的场景
      FirebaseMessaging.instance.getInitialMessage().then((value) {
        if (value?.notification != null) {
          notific = {
            'title': value!.notification!.title,
            'body': value.notification!.body
          };
          notifList.add(notific);
          setState(() {});
          _saveNotifications();
        }
      });
    
      // 前台收到消息的场景
      FirebaseMessaging.onMessage.listen((RemoteMessage message) {
        if (message.notification != null) {
          notific = {
            'title': message.notification!.title,
            'body': message.notification!.body
          };
          notifList.add(notific);
          setState(() {
            title = message.notification!.title;
            body = message.notification!.body;
          });
          // 列表更新后立刻存储
          _saveNotifications();
          print('MENSAGEM: $notific');
        }
      });
    
      // 后台点击通知打开app的场景
      FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
        if (message.notification != null) {
          notific = {
            'title': message.notification!.title,
            'body': message.notification!.body
          };
          notifList.add(notific);
          setState(() {});
          _saveNotifications();
        }
      });
    }
    
  4. 调整initState的执行顺序
    打开页面时先加载本地存的历史通知,再启动FCM消息监听,不要在initState里直接执行存储逻辑:
    @override
    void initState() {
      super.initState();
      _loadSavedNotifications().then((_) => fcmMessageReceiver());
    }
    

实用优化建议

  • 限制通知存储条数:如果长期累积通知列表过长,会导致JSON字符串过大,拖慢SharedPreferences的读写速度,可以在每次加新通知后判断长度,比如只保留最近50条,超过就删掉最早的条目:
    // 加完新通知后执行
    if (notifList.length > 50) notifList.removeAt(0);
    
  • 做空安全适配:FCM推送的通知标题、内容都是可空值,转Map的时候不要硬转成非空字符串,避免遇到无内容的推送时直接崩溃
  • 读取时机优化:如果通知页面不是打开app就必须展示的页面,可以等用户点击进入通知页时再触发本地数据读取,减少app启动时的加载耗时

内容的提问来源于stack exchange,提问作者Kaya Ryuuna

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.25 23:45:03