Flutter搜索API数据无法显示ListView,类型转换错误求助
搜索功能API数据无法显示的问题排查
问题场景
我是Flutter新手,在实现搜索功能时,API已返回正常JSON数据,但ListView无法加载显示,同时抛出以下类型错误:
Expected a value of type '(String, dynamic) => MapEntry<dynamic, dynamic>', but got one of type '(dynamic) => SearchModel$'
API返回的JSON片段
{ "konten_gambar": null, "konten_gambar2": null, "konten_gambar3": null, "konten_deskripsi": null, "konten_syarat": null, "konten_ketentuan": null, "konten_fasilitas": null, "konten_promosi_gambar": null, "konten_promosi_text": null, "konten_sk": null, "konten_status": null, "konten_update": null, "konten_approval": null, "konten_url": "/danakonven", "file_path": null, "konten_deskripsi2": null, "konten_deskripsi3": null, "file_path2": null, "file_path3": null }
错误信息
Expected a value of type '(String, dynamic) => MapEntry<dynamic, dynamic>', but got one of type '(dynamic) => SearchModel$'
源码
model.dart
import 'dart:convert'; List<SearchModel> searchModelFromJson(String str) => List<SearchModel>.from( // ignore: always_specify_types json.decode(str).map((x) => SearchModel.fromJson(x))); String searchModelJson(List<SearchModel> data) => json.encode(List<dynamic>.from(data.map((SearchModel x) => x.toJson()))); class SearchModel { SearchModel({ this.kontenId, this.kontenParent, this.kontenUrut, this.kategoriId, this.kontenSimulasi, this.kontenSukuBunga, required this.kontenMenu, this.kontenJudul, this.kontenSubjudul, this.kontenGambar, this.kontenGambar2, this.kontenGambar3, this.kontenDeskripsi, this.kontenSyarat, this.kontenKetentuan, this.kontenFasilitas, this.kontenPromosiGambar, this.kontenPromosiText, this.kontenSk, this.kontenStatus, this.kontenUpdate, this.kontenApproval, this.kontenUrl, this.filePath, this.kategoriNama, }); factory SearchModel.fromJson(Map<String, dynamic> json) => SearchModel( kontenId: json['konten_id'], kontenParent: json['konten_parent'], kontenUrut: json['konten_urut'], kategoriId: json['kategori_id'], kontenSimulasi: json['konten_simulasi'], kontenSukuBunga: json['konten_suku_bunga'], kontenMenu: json['konten_menu'], kontenJudul: json['konten_judul'], kontenSubjudul: json['konten_subjudul'], kontenGambar: json['konten_gambar'], kontenGambar2: json['konten_gambar2'], kontenGambar3: json['konten_gambar3'], kontenDeskripsi: json['konten_deskripsi'], kontenSyarat: json['konten_syarat'], kontenKetentuan: json['konten_ketentuan'], kontenFasilitas: json['konten_fasilitas'], kontenPromosiGambar: json['konten_promosi_gambar'], kontenPromosiText: json['konten_promosi_text'], kontenSk: json['konten_sk'], kontenStatus: json['konten_status'], kontenUpdate: json['konten_update'], kontenApproval: json['konten_approval'], kontenUrl: json['konten_url'], filePath: json['file_path'], kategoriNama: json['kategori_nama'], ); dynamic kontenId; dynamic kontenParent; dynamic kontenUrut; dynamic kategoriId; dynamic kontenSimulasi; dynamic kontenSukuBunga; String kontenMenu; dynamic kontenJudul; dynamic kontenSubjudul; dynamic kontenGambar; dynamic kontenGambar2; dynamic kontenGambar3; dynamic kontenDeskripsi; dynamic kontenSyarat; dynamic kontenKetentuan; dynamic kontenFasilitas; dynamic kontenPromosiGambar; dynamic kontenPromosiText; dynamic kontenSk; dynamic kontenStatus; dynamic kontenUpdate; dynamic kontenApproval; dynamic kontenUrl; dynamic filePath; dynamic kategoriNama; Map<String, dynamic> toJson() => { 'konten_id': kontenId, 'konten_parent': kontenParent, 'konten_urut': kontenUrut, 'kategori_id': kategoriId, 'konten_simulasi': kontenSimulasi, 'konten_suku_bunga': kontenSukuBunga, 'konten_menu': kontenMenu, 'konten_judul': kontenJudul, 'konten_subjudul': kontenSubjudul, 'konten_gambar': kontenGambar, 'konten_gambar2': kontenGambar2, 'konten_gambar3': kontenGambar3, 'konten_deskripsi': kontenDeskripsi, 'konten_syarat': kontenSyarat, 'konten_ketentuan': kontenKetentuan, 'konten_fasilitas': kontenFasilitas, 'konten_promosi_gambar': kontenPromosiGambar, 'konten_promosi_text': kontenPromosiText, 'konten_sk': kontenSk, 'konten_status': kontenStatus, 'konten_update': kontenUpdate, 'konten_approval': kontenApproval, 'konten_url': kontenUrl, 'file_path': filePath, 'kategori_nama': kategoriNama, }; }
apiservice.dart
class SearchService { Future<SearchModel> searchMenu(String kontenMenu) async { try { final String apiUrl = '${baseUrl}search/$kontenMenu'; final http.Response response = await httpClient.get( Uri.parse(apiUrl), // ignore: always_specify_types headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); debugPrint(response.body); if (response.statusCode == 200 || response.statusCode == 201) { // ignore: always_specify_types final it = json.decode(response.body); final List<SearchModel> list = // ignore: always_specify_types it.map((item) => SearchModel.fromJson(item))[0].toList();
问题排查与修复
1. API服务层核心错误
- 返回类型不匹配:
searchMenu方法声明返回Future<SearchModel>,但实际要处理的是列表数据,应改为Future<List<SearchModel>>。 - 解析逻辑错误:
it.map((item) => SearchModel.fromJson(item))返回的是可迭代对象,直接加[0].toList()会把第一个SearchModel实例当成列表处理,触发类型转换错误。
修复后的apiservice.dart片段:
class SearchService { // 修改返回类型为列表 Future<List<SearchModel>> searchMenu(String kontenMenu) async { try { final String apiUrl = '${baseUrl}search/$kontenMenu'; final http.Response response = await httpClient.get( Uri.parse(apiUrl), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); debugPrint(response.body); if (response.statusCode == 200 || response.statusCode == 201) { final dynamic it = json.decode(response.body); // 若API返回的是包含数组的对象(如{"data": [...]}),需改为it['data'].map(...) final List<SearchModel> list = List<SearchModel>.from( it.map((item) => SearchModel.fromJson(item)) ); return list; } else { throw Exception('请求失败'); } } catch (e) { rethrow; } } }
2. Model类必填字段问题
从API返回的JSON看,缺少konten_menu字段,但Model中该字段被标记为required,会导致解析时抛出空值异常。需修改为可选字段并设置默认值:
// 修改构造函数参数,去掉required SearchModel({ this.kontenId, this.kontenParent, this.kontenUrut, this.kategoriId, this.kontenSimulasi, this.kontenSukuBunga, this.kontenMenu, this.kontenJudul, // ...其他参数 }); // fromJson中处理空值 factory SearchModel.fromJson(Map<String, dynamic> json) => SearchModel( // ...其他字段 kontenMenu: json['konten_menu'] ?? '', // 给默认空字符串 // ...其他字段 );
3. 错误根源总结
- 服务层返回类型与实际数据结构不匹配,解析逻辑错误触发类型转换异常。
- Model中必填字段
kontenMenu在API返回中缺失,会引发空值错误。
内容的提问来源于stack exchange,提问作者Lussy Ika
相关产品推荐
相关产品推荐

