Flutter Fetch API报错:RangeError与类型不匹配问题求助
解决Flutter Fetch API中的地址显示问题
问题描述
- 无法获取下一个用户的初始默认地址(shipto=0),当地址列表数量较多时频繁出现;
- 尝试修复后出现类型不匹配错误:
type 'string' is not a subtype of type 'int' of 'index'
原代码
主Widget代码
List<Items> _list = []; List<Items> _search = []; var loading = false; Future fetchMos() async { setState(() { loading = true; }); _list.clear(); var client = http.Client(); String mosUrl = ''; var url = Uri.parse(mosUrl); var headers = { 'Client-Token': '', }; var response = await client.get(url, headers: headers); if (response.statusCode == 200) { var data = jsonDecode((utf8.decode(response.bodyBytes)))['items']; setState(() { for (var index in data) { _list.add(Items.fromJson(index)); loading = false; } }); } } @override void initState() { super.initState(); fetchMos(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( elevation: 0.0, ), body: ListView.builder( itemBuilder: (context, index) { if (_list.length > 0) { return _listItem(index); } else { return Center(child: CircularProgressIndicator()); } }, itemCount: _list.length, ), ); }
_listItem方法
_listItem(index) { return Card( elevation: 3, child: Column( children: <Widget>[ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[ Padding( padding: const EdgeInsets.only(left: 5), child: Text( "${_list[index].name} ${_list[index].custnum}", style: TextStyle( color: Colors.black, fontSize: 18, fontFamily: "supermarket"), ), ), TextButton( child: const Text( 'แก้ไข', style: TextStyle(fontFamily: "supermarket", fontSize: 14), ), onPressed: () {/* ... */}, ), ], ), Padding( padding: const EdgeInsets.all(5), child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Text( 'bill To / Ship To : ${_list[index].address![index].shipto.toString()}', style: TextStyle( color: Colors.black, fontSize: 16, fontFamily: "supermarket"), ), ], ), ), Padding( padding: const EdgeInsets.all(5), child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Text( "ที่อยู่ : ${_list[index].address![index].addr1.toString()}", style: TextStyle( color: Colors.black, fontSize: 16, fontFamily: "supermarket"), ), ], ), ), ], ), ); }
返回的JSON数据
{ "items": [ { "custnum": "", "name": "", "address": [ { "shipto": 0, "addr1": "", "thanon": "", "tambon": "", "amphur": "", "prov_code": "", "province": "", "zipcode": "", "country": "", "contact": "", "postcode": "" }, { "shipto": 1, "addr1": "", "thanon": "", "tambon": "", "amphur": "", "prov_code": "", "province": "", "zipcode": "", "country": "", "contact": "", "postcode": "" }, { "shipto": 2, "addr1": "", "thanon": "", "tambon": "", "amphur": "", "prov_code": "", "province": "", "zipcode": "", "country": "", "contact": "", "postcode": "" }, { "shipto": 3, "addr1": "", "thanon": "", "tambon": "", "amphur": "", "prov_code": "", "province": "", "zipcode": "", "country": "", "contact": "", "postcode": "" } ] }, { "custnum": "", "name": "", "address": [ { "shipto": 0, "addr1": "", "thanon": "", "tambon": "", "amphur": "", "prov_code": "", "province": "", "zipcode": "", "country": "", "contact": "", "postcode": "" }, { "shipto": 1, "addr1": "", "thanon": "", "tambon": "", "amphur": "", "prov_code": "", "province": "", "zipcode": "", "country": "", "contact": "", "postcode": "" }, { "shipto": 2, "addr1": "", "thanon": "", "tambon": "", "amphur": "", "prov_code": "", "province": "", "zipcode": "", "country": "", "contact": "", "postcode": "" }, { "shipto": 3, "addr1": "", "thanon": "", "tambon": "", "amphur": "", "prov_code": "", "province": "", "zipcode": "", "country": "", "contact": "", "postcode": "" } ] }
问题根源与修复方案
核心问题
- 索引混用:
_listItem中用同一个index同时访问用户列表和地址列表,导致第二个用户会去取自己的第1个地址(而非默认的第0个),当用户列表的索引超过地址列表长度时,直接触发类型/越界错误。 - 状态更新不当:
fetchMos中每次循环都调用setState,性能低下且可能导致状态异常。
修复后的代码
1. 修正_listItem方法(固定取默认地址+空安全)
Widget _listItem(int index) { final user = _list[index]; // 确保地址列表非空时取第0个默认地址 final defaultAddress = user.address?.isNotEmpty == true ? user.address![0] : null; return Card( elevation: 3, child: Column( children: <Widget>[ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[ Padding( padding: const EdgeInsets.only(left: 5), child: Text( "${user.name} ${user.custnum}", style: TextStyle( color: Colors.black, fontSize: 18, fontFamily: "supermarket"), ), ), TextButton( child: const Text( 'แก้ไข', style: TextStyle(fontFamily: "supermarket", fontSize: 14), ), onPressed: () {/* ... */}, ), ], ), // 只有默认地址存在时才显示地址相关内容 if (defaultAddress != null) ...[ Padding( padding: const EdgeInsets.all(5), child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Text( 'bill To / Ship To : ${defaultAddress.shipto.toString()}', style: TextStyle( color: Colors.black, fontSize: 16, fontFamily: "supermarket"), ), ], ), ), Padding( padding: const EdgeInsets.all(5), child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Text( "ที่อยู่ : ${defaultAddress.addr1.toString()}", style: TextStyle( color: Colors.black, fontSize: 16, fontFamily: "supermarket"), ), ], ), ), ], ], ), ); }
2. 优化fetchMos方法(批量更新状态+错误处理)
Future fetchMos() async { setState(() { loading = true; }); _list.clear(); var client = http.Client(); String mosUrl = ''; var url = Uri.parse(mosUrl); var headers = { 'Client-Token': '', }; var response = await client.get(url, headers: headers); setState(() { if (response.statusCode == 200) { var data = jsonDecode((utf8.decode(response.bodyBytes)))['items']; for (var item in data) { _list.add(Items.fromJson(item)); } } // 无论请求成功失败,都重置loading状态 loading = false; }); }
修复说明
- 把
_list[index].address![index]改为_list[index].address![0],确保每个用户都显示自己的默认地址(shipto=0),解决第一个问题; - 添加空安全判断,避免地址列表为空时触发异常;
- 将
loading = false移出循环,仅在所有数据处理完成后更新状态,提升性能; - 新增请求失败的状态处理,避免loading一直处于true状态。
内容的提问来源于stack exchange,提问作者Ppnncs
相关产品推荐
相关产品推荐

