Flutter调用API报错String非Map<String,dynamic>子类型排查修复
报错根因
这个类型异常触发点在JSON解析环节,核心是类型不匹配:
你当前的解析逻辑默认jsonDecode(response.body)返回对象的所有顶级字段值都是Map<String, dynamic>类型,无差别遍历所有values传入Modelclass.fromJson(该方法参数要求是Map类型)。但实际接口返回的顶级JSON是混合结构,包含String类型的字段(比如接口声明、图表名称这类文本字段),当遍历到String类型值传入fromJson时,就会抛出type 'String' is not a subtype of type 'Map<String, dynamic>'的未处理异常。
结合你给出的实现,还有两个连带逻辑错误:
- 你定义的
Modelclass本身就是映射整个接口顶级返回结构的类,不需要遍历values构造模型列表 - 页面层用
state.modelclass[2]索引取值,说明你错误将解析结果当成了同质列表,就算遍历阶段没触发类型错误,得到的集合也是混合了嵌套对象、字符串的无序集合,索引2对应的是字符串值,访问.time属性依然会报错。
问题代码定位
你当前Repo层的解析逻辑大概率是下面这种错误写法:
// 错误实现 final response = await http.get(Uri.parse('接口地址')); if (response.statusCode == 200) { final decoded = jsonDecode(response.body); // 问题1:未校验解码结果类型,默认所有value都是Map结构 // 问题2:无差别遍历顶级values,把字符串等非Map值传入fromJson final List<Modelclass> modelList = decoded.values.map((e) => Modelclass.fromJson(e)).toList(); return modelList; }
从你定义的Modelclass、Time、Bpi、USD四个类判断,你调用的是比特币价格查询类接口,该接口顶级返回结构是对象而非数组,包含time(嵌套对象)、disclaimer(字符串)、chartName(字符串)、bpi(嵌套对象)四个顶级字段,遍历values会依次拿到Map、String、String、Map四种不同类型的值。
修正方案
按以下步骤调整代码即可解决问题:
- 修正Repo层解析逻辑,不要遍历顶级values,直接将整个解码后的Map传入顶层模型的fromJson方法
// 正确的Repo实现 Future<Modelclass> fetchBpiData() async { final response = await http.get(Uri.parse('你的接口请求地址')); if (response.statusCode == 200) { // 显式做类型强转,提前校验解码结果格式 final Map<String, dynamic> decodedData = jsonDecode(response.body) as Map<String, dynamic>; // 直接传入整个解码结果构造顶层模型,无需遍历 return Modelclass.fromJson(decodedData); } else { throw Exception('请求失败,状态码:${response.statusCode}'); } }
- 修正所有模型类的fromJson方法,严格按字段层级做类型转换,不要跨层级传值
// 顶层模型示例,其余Time、Bpi、USD类按相同逻辑编写 class Modelclass { final Time time; final String disclaimer; final String chartName; final Bpi bpi; Modelclass({ required this.time, required this.disclaimer, required this.chartName, required this.bpi, }); factory Modelclass.fromJson(Map<String, dynamic> json) { return Modelclass( // 嵌套对象单独调用对应类的fromJson,传入对应字段的Map值 time: Time.fromJson(json['time'] as Map<String, dynamic>), disclaimer: json['disclaimer'] as String, chartName: json['chartName'] as String, bpi: Bpi.fromJson(json['bpi'] as Map<String, dynamic>), ); } }
- 修正页面层渲染逻辑,不要用索引取值,直接访问模型实例的属性
// 错误写法:Text(state.modelclass[2].time.toString()) // 正确写法 Text(state.modelclass.time.toString()) // 如果需要展示具体时间字段,比如更新时间,可直接访问子属性:Text(state.modelclass.time.updated)
通用JSON解析注意事项
- 写解析逻辑前先打印解码后的数据结构和runtimeType,确认每个字段的实际类型,不要靠猜写代码
- 所有类型转换都加显式
as强转,让类型不匹配问题在解析阶段就暴露,不要等到渲染层才触发异常 - 只有当接口顶级返回的是JSON数组(解码后runtimeType为List)时,才遍历列表项构造模型列表;如果顶级是对象,直接传入对应顶层模型fromJson即可,不要遍历对象values
- 解析前增加类型判断逻辑,比如先判断解码结果是Map还是List,再走对应解析分支,避免接口结构变动直接触发崩溃。
内容的提问来源于stack exchange,提问作者Diljith
相关产品推荐
相关产品推荐

