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

Flutter本地存储报错:_InternalLinkedHashMap非Map<String,double>子类型

根因分析

异常直接触发点是Product.fromJson构造函数中productPrices字段的强制类型转换,本质是Dart内置jsonDecode的反序列化规则导致的类型不匹配:

  • 调用jsonDecode解析本地存储的JSON字符串时,所有嵌套的Map结构默认会被解析为_InternalLinkedHashMap<String, dynamic>类型。即使序列化前对象是Map<String, double>类型,反序列化后内层Map的value类型也会被重置为dynamic,不会自动保留double泛型。
  • 原有代码直接执行map['productPrices'] as Map<String, double>强转,相当于把Map<String, dynamic>直接转换为Map<String, double>,运行时类型不匹配直接抛出异常。
  • 代码还存在一处隐藏逻辑缺陷:静态内存集合_shoppingListSet不会在App启动时从本地文件加载初始化,App重启后第一次执行写入操作会直接覆盖本地原有数据。
可落地修复方案

1. 修复Product类反序列化的类型转换逻辑

禁止直接强转嵌套Map的泛型,需要逐元素做类型转换,同时兼容数字类型的自动适配(JSON序列化时整数类型的价格可能被存为int而非double)。
将Product.fromJson原有实现替换为如下代码:

Product.fromJson(Map<String, dynamic> map)
    :   user = map['user'] as String,
        id = (map['id'] as num).toInt(),
        name = map['name'] as String,
        image = map['image'] as String,
        brandName = map['brandName'] as String,
        // 先转成dynamic类型的Map,再逐个将value转为double
        productPrices = Map<String, double>.from(
          (map['productPrices'] as Map<String, dynamic>).map(
            (key, value) => MapEntry(key, (value as num).toDouble()),
          ),
        );

2. 补全ShoppingListHandler的内存集合初始化逻辑

解决App重启后内存集合为空导致的数据覆盖问题:

  • 新增初始化方法,在App启动完成后第一时间调用,将本地存储的数据加载到内存:
// 新增初始化方法,建议在runApp执行后立即调用
Future<void> init() async {
  final storedProducts = await readShoppingListProducts();
  _shoppingListSet = storedProducts.toSet();
}
  • 修改readShoppingListProducts方法,读取本地数据后同步更新内存集合,保证内存数据和本地文件一致:
Future<List<ShoppingListProduct>> readShoppingListProducts() async {
  final File fl = await file;
  final _content = await fl.readAsString();

  List<dynamic> _jsonData = [];
  if(_content.isNotEmpty){
    _jsonData = jsonDecode(_content);
  }

  final List<ShoppingListProduct> _shoppingListProducts = _jsonData
      .map(
        (e) => ShoppingListProduct.fromJson(e as Map<String, dynamic>),
      )
      .toList();
  // 同步更新内存集合
  _shoppingListSet = _shoppingListProducts.toSet();
  return _shoppingListProducts;
}

3. (可选)增加旧数据兼容兜底

如果应用已经发布过旧版本,本地可能存在格式异常的历史数据,可以给类型转换增加兜底逻辑,避免极端场景崩溃:

// 带异常兜底的productPrices转换逻辑
productPrices = () {
  final rawData = map['productPrices'];
  // 字段不存在或类型不对时返回空Map
  if (rawData is! Map) return <String, double>{};
  return Map<String, double>.from(
    rawData.map(
      (key, value) {
        // 兼容字符串、int、double等多种可能的value格式
        final parsedPrice = num.tryParse(value.toString())?.toDouble() ?? 0.0;
        return MapEntry(key.toString(), parsedPrice);
      },
    ),
  );
}(),

内容的提问来源于stack exchange,提问作者Jack .T

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 13:06:26