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

Flutter基于GetX与GetStorage读取本地列表时触发类型不匹配报错

核心问题根因

报错本质是存储读取时的类型不匹配,和GetX页面栈的路由逻辑没有直接关系,具体是两个叠加的问题导致的:

  • 写入GetStorage时,没有将自定义的ShoppingList实例手动转换为可被Storage序列化的Map<String, dynamic>结构,直接把内存中的对象列表存入了本地存储。
  • 从Storage读取数据时,代码默认列表里的元素全是Map<String, dynamic>结构,实际因为之前写入的是未转换的ShoppingList对象,调用ShoppingList.fromjson()时传入了类型不匹配的参数,直接触发类型报错。

这个报错的复现逻辑和你的操作完全匹配:首次打开页面时直接使用内存中的对象,不触发Storage读取逻辑所以不报错;退出页面时因为写入的是未转换的原始对象,本地存储的格式不符合预期;再次进入页面时执行Storage读取流程,传入类型不匹配的参数就触发了崩溃。

分步修复方案
  • 第一步:补全ShoppingList类的序列化逻辑
    原代码的toJson方法漏了done字段的序列化,就算解决了类型不匹配问题,也会导致任务完成状态永久丢失。修正后的类定义如下:
    class ShoppingList {
      String text;
      bool done;
    
      ShoppingList({required this.text, this.done = false});
    
      // 修正命名为标准的fromJson,避免拼写歧义
      factory ShoppingList.fromJson(Map<String, dynamic> json) => ShoppingList(
        text: json['text'] as String,
        done: json['done'] as bool,
      );
    
      Map<String, dynamic> toJson() {
        final Map<String, dynamic> data = <String, dynamic>{};
        data['text'] = text;
        data['done'] = done; // 补上遗漏的done字段序列化
        return data;
      }
    }
    
  • 第二步:修正存储写入逻辑
    GetStorage仅支持基础类型、嵌套Map/List结构的持久化,无法直接序列化自定义Dart类对象,写入时必须手动将每个ShoppingList实例转为Map结构再存入:
    // 控制器内新增存储同步方法
    void syncDataToStorage() {
      // 将所有购物项转成Map结构生成可存储的列表
      final List<Map<String, dynamic>> storageMapList = shoppings
          .map((item) => item.toJson())
          .toList();
      GetStorage().write('shoppings', storageMapList);
    }
    
    在新增条目、修改完成状态、删除条目等列表变更的位置,调用这个同步方法,保证存入Storage的永远是纯Map结构的列表。
  • 第三步:增强读取逻辑的健壮性
    原控制器的onInit存在两个问题:没有调用父类生命周期方法、没有做异常类型过滤,容易因为历史脏数据触发崩溃。修正后的控制器代码如下:
    class ShoppingListController extends GetxController {
      // 明确声明RxList类型,避免类型推断错误
      final RxList<ShoppingList> shoppings = <ShoppingList>[].obs;
    
      @override
      void onInit() {
        super.onInit(); // 必须调用父类onInit完成生命周期初始化
        final List? storedShoppings = GetStorage().read<List>('shoppings');
    
        if (storedShoppings != null) {
          // 过滤掉列表中非Map结构的脏数据,避免类型报错
          shoppings.value = storedShoppings
              .whereType<Map<String, dynamic>>()
              .map((e) => ShoppingList.fromJson(e))
              .toList();
        }
      }
    
      // 示例:新增购物项的方法,变更后自动同步存储
      void addItem(String content) {
        shoppings.add(ShoppingList(text: content));
        syncDataToStorage();
      }
    }
    
额外注意事项
  • 不要直接将自定义类实例直接传入GetStorage().write(),必须手动完成对象到Map的转换,否则下次读取必然出现类型异常。
  • 原代码中给响应式列表重新赋值的写法(shoppings = xxx.toList().obs)虽然能运行,但更推荐通过shoppings.value = xxx的方式赋值,避免出现响应式绑定失效的问题。
  • 本地存储的key要保持全局唯一,避免和其他存储值混淆。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.26 14:57:00