Flutter中RangeError(index)报错排查:API列表展示异常解决
问题排查与解决方案
一、错误本质解析
RangeError (index): Invalid value: Only valid value is 0: 2 是数组索引越界错误:你的数据源数组长度仅为1(仅索引0有效),但代码尝试访问索引2的位置。常见触发场景:
- ListView的
itemCount写死固定值(比如3),与接口返回的实际列表长度不匹配 - 手动遍历数组时,循环次数超过数组实际长度
- 数据解析错误,将非数组结构当成数组处理,或数组嵌套层级解析错误
二、结合代码/JSON/模型的排查要点
假设你提供的代码、JSON、模型存在以下典型问题,对应排查方向:
1. 接口返回JSON示例(常见结构)
{
"code": 200,
"data": {
"list": [{"id":1, "name":"测试1"}]
}
}
2. 数据模型示例(Dart)
class ApiResponse { int code; Data data; ApiResponse({required this.code, required this.data}); factory ApiResponse.fromJson(Map<String, dynamic> json) => ApiResponse( code: json["code"], data: Data.fromJson(json["data"]), ); } class Data { List<Item> list; Data({required this.list}); factory Data.fromJson(Map<String, dynamic> json) => Data( list: List<Item>.from(json["list"].map((x) => Item.fromJson(x))), ); } class Item { int id; String name; Item({required this.id, required this.name}); factory Item.fromJson(Map<String, dynamic> json) => Item( id: json["id"], name: json["name"], ); }
3. 常见错误代码示例
// 错误:itemCount写死为3,实际列表长度仅为1 ListView.builder( itemCount: 3, itemBuilder: (context, index) { return Text(apiResponse.data.list[index].name); }, ) // 错误:循环次数硬编码,超过数组长度 for (int i = 0; i < 3; i++) { print(apiResponse.data.list[i].name); }
排查步骤
- 打印数组长度:在使用数组前执行
print(apiResponse.data.list.length),确认实际数据量 - 检查
itemCount:ListView的itemCount必须严格绑定数组长度,比如itemCount: apiResponse.data.list.length - 验证数据解析:确认模型正确解析了接口返回的数组,比如是否将
data.list正确转为List<Item>,而非把外层对象当作数组
三、避免红屏错误的方案
- 空值与长度前置判断
在渲染列表前先做安全校验,避免空指针和越界:
if (apiResponse.data.list != null && apiResponse.data.list.isNotEmpty) { // 渲染列表 } else { return Center(child: Text("暂无数据")); }
- ListView安全写法
确保itemCount与数组长度绑定,同时添加索引兜底判断:
ListView.builder( itemCount: apiResponse.data.list?.length ?? 0, itemBuilder: (context, index) { if (index >= (apiResponse.data.list?.length ?? 0)) return SizedBox.shrink(); return Text(apiResponse.data.list[index].name); }, )
- 请求阶段异常捕获
在API请求时捕获网络错误、解析错误,通过状态变量切换UI,避免直接抛出红屏:
Future<void> fetchData() async { try { setState(() => _isLoading = true); final response = await http.get(Uri.parse("你的API地址")); if (response.statusCode == 200) { final jsonData = json.decode(response.body); _apiResponse = ApiResponse.fromJson(jsonData); } else { setState(() => _errorMsg = "请求失败"); } } catch (e) { setState(() => _errorMsg = "加载出错:$e"); } finally { setState(() => _isLoading = false); } } // 渲染时根据状态切换 if (_isLoading) { return Center(child: CircularProgressIndicator()); } else if (_errorMsg != null) { return Center(child: Text(_errorMsg!)); } else { // 渲染列表 }
- 空安全操作符兜底
使用Dart空安全特性的?.和??,避免空指针和无效长度:
final listLength = apiResponse.data.list?.length ?? 0;
四、正确列表展示示例
class MyListPage extends StatefulWidget { @override _MyListPageState createState() => _MyListPageState(); } class _MyListPageState extends State<MyListPage> { ApiResponse? _apiResponse; bool _isLoading = false; String? _errorMsg; @override void initState() { super.initState(); fetchData(); } Future<void> fetchData() async { try { setState(() => _isLoading = true); final response = await http.get(Uri.parse("https://your-api-url.com/list")); if (response.statusCode == 200) { final jsonData = json.decode(response.body); _apiResponse = ApiResponse.fromJson(jsonData); } else { setState(() => _errorMsg = "请求失败,状态码:${response.statusCode}"); } } catch (e) { setState(() => _errorMsg = "加载出错:$e"); } finally { setState(() => _isLoading = false); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("列表展示")), body: _buildBody(), ); } Widget _buildBody() { if (_isLoading) { return const Center(child: CircularProgressIndicator()); } if (_errorMsg != null) { return Center(child: Text(_errorMsg!)); } final list = _apiResponse?.data.list ?? []; if (list.isEmpty) { return const Center(child: Text("暂无数据")); } return ListView.builder( itemCount: list.length, itemBuilder: (context, index) { final item = list[index]; return ListTile( title: Text(item.name), subtitle: Text("ID: ${item.id}"), ); }, ); } }
内容的提问来源于stack exchange,提问作者Aashi
相关产品推荐
相关产品推荐

