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

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>,而非把外层对象当作数组

三、避免红屏错误的方案

  1. 空值与长度前置判断
    在渲染列表前先做安全校验,避免空指针和越界:
if (apiResponse.data.list != null && apiResponse.data.list.isNotEmpty) {
  // 渲染列表
} else {
  return Center(child: Text("暂无数据"));
}
  1. 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);
  },
)
  1. 请求阶段异常捕获
    在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 {
  // 渲染列表
}
  1. 空安全操作符兜底
    使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 11:13:14