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

Flutter RangeError(index)问题排查:非ListView.Builder场景索引越界

解决RangeError (index): Invalid value: Range is empty错误

问题描述

应用运行正常,但会抛出RangeError (index): Invalid value: Range is empty错误。使用FutureBuilder从数据库读取数据,默认展示列表第一条,通过初始值为0的计数器切换数据。怀疑是空列表访问索引导致,但网上方案多针对ListView.Builder,未找到适配场景的解决办法。

相关代码

// 计数器变量声明
var index = 0;

// 数据库读取函数
 Future<List?> read(String query) async {
    var result = await SqlConn.readData(query);
    query.replaceAll("\"", "");
    List _list = jsonDecode(result) as List;
    debugPrint('${_list.length} <===== size');
    return _list;
  }

// 数据展示部分代码
child: FutureBuilder<List?>(
                    future: read(
                        "SELECT ProductAdress, replace(ProductName, '\"', '')ProductName, NeedCount, ScanCount, ProductBarCode, ProductSeriesCount, ProductExpirationDate FROM ScanRest WHERE ProductStation = '${widget.nrStatie}' AND BoxID = '$cutieScan' Order By ProductName ASC"),
                    builder: (context, snapshot) {
                      switch (snapshot.connectionState) {
                        case ConnectionState.waiting:
                          return const Text('Loading....');
                        default:
                          if (snapshot.hasError) {
                            debugPrint(
                                "call error");
                            return Text('Error: ${snapshot.error}');
                          } else {
                            debugPrint(
                                "call success");
                            List data = snapshot.data ?? [];
                            return Column(children: [
                              Row(
                                children: [
                                  // 产品地址
                                  Expanded(
                                      child: GestureDetector(
                                    onTap: () {
                                      setState(() {
                                        i++;
                                        if (i == snapshot.data!.length) {
                                          i = 0;
                                        }
                                      });
                                    },
                                    child: SizedBox(
                                        height: 60,
                                        child: Center(
                                          child: Text( 'i=' +i.toString() + " " + ((data[i] as Map)['ProductAdress'].toString()),
                                            style: const TextStyle(fontSize: 30),
                                          ),
                                        )),
                                  )),

                                  // 需求数量
                                  Expanded(
                                      child: GestureDetector(
                                    onTap: () {
                                      _nrProdusController.text =
                                          (data[i] as Map)['NeedCount'].toString();
                                    },
                                    child: SizedBox(
                                      height: 60,
                                      child: Center(
                                        child: Text(
                                          ((data[i] as Map)['NeedCount']
                                              .toString()),
                                          style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold, color: Colors.primaries[Random().nextInt(Colors.primaries.length)]),
                                        ),
                                      ),
                                    ),
                                  )),
                                ],
                              ),

问题根源

  1. 空列表直接访问索引:当数据库查询无结果时,data = snapshot.data ?? []会变成空列表,但代码直接使用data[i](i初始为0),触发空列表的RangeError。
  2. 计数器逻辑隐患:切换时用snapshot.data!.length强制解包,若snapshot.data为空会触发空指针;同时计数器变量index和i不一致,易引发状态混乱。
  3. FutureBuilder重复请求:每次组件重建都会调用read()重新查询,既浪费资源也可能导致数据状态不一致。

修复方案

1. 新增空列表判断

在获取数据后先检查列表是否为空,避免访问无效索引:

List data = snapshot.data ?? [];
if (data.isEmpty) {
  return const Center(child: Text('暂无数据'));
}

2. 修复计数器逻辑

  • 统一计数器变量名,避免混淆;
  • 用data.length代替snapshot.data!.length,避免强制解包;
  • 确保计数器不越界:
// 统一计数器变量
int currentIndex = 0;

// 点击切换逻辑
setState(() {
  currentIndex++;
  if (currentIndex == data.length) {
    currentIndex = 0;
  }
});

// 额外防护:确保currentIndex在有效范围内
if (currentIndex >= data.length) {
  currentIndex = 0;
}

3. 优化FutureBuilder请求

在initState中提前初始化查询Future,避免组件重建时重复请求:

late Future<List?> _fetchDataFuture;

@override
void initState() {
  super.initState();
  _fetchDataFuture = read(
    "SELECT ProductAdress, replace(ProductName, '\"', '')ProductName, NeedCount, ScanCount, ProductBarCode, ProductSeriesCount, ProductExpirationDate FROM ScanRest WHERE ProductStation = '${widget.nrStatie}' AND BoxID = '$cutieScan' Order By ProductName ASC"
  );
}

// FutureBuilder中使用初始化好的future
child: FutureBuilder<List?>(
  future: _fetchDataFuture,
  // ... 其他代码
)

4. 修正无效代码

原代码中query.replaceAll("\"", "")无实际作用,replaceAll返回新字符串,原变量不会修改。若需处理结果中的引号,应在jsonDecode前处理result:

// 修改read函数
Future<List?> read(String query) async {
  var result = await SqlConn.readData(query);
  // 处理结果中的引号
  result = result.replaceAll("\"", "");
  List _list = jsonDecode(result) as List;
  debugPrint('${_list.length} <===== size');
  return _list;
}

完整修复后代码片段

int currentIndex = 0;
late Future<List?> _fetchDataFuture;

@override
void initState() {
  super.initState();
  _fetchDataFuture = read(
    "SELECT ProductAdress, replace(ProductName, '\"', '')ProductName, NeedCount, ScanCount, ProductBarCode, ProductSeriesCount, ProductExpirationDate FROM ScanRest WHERE ProductStation = '${widget.nrStatie}' AND BoxID = '$cutieScan' Order By ProductName ASC"
  );
}

Future<List?> read(String query) async {
  var result = await SqlConn.readData(query);
  result = result.replaceAll("\"", "");
  List _list = jsonDecode(result) as List;
  debugPrint('${_list.length} <===== size');
  return _list;
}

// 数据展示部分
child: FutureBuilder<List?>(
  future: _fetchDataFuture,
  builder: (context, snapshot) {
    switch (snapshot.connectionState) {
      case ConnectionState.waiting:
        return const Text('加载中....');
      default:
        if (snapshot.hasError) {
          debugPrint("请求错误");
          return Text('错误: ${snapshot.error}');
        } else {
          debugPrint("请求成功");
          List data = snapshot.data ?? [];
          if (data.isEmpty) {
            return const Center(child: Text('暂无数据'));
          }
          if (currentIndex >= data.length) {
            currentIndex = 0;
          }
          return Column(children: [
            Row(
              children: [
                Expanded(
                    child: GestureDetector(
                  onTap: () {
                    setState(() {
                      currentIndex++;
                      if (currentIndex == data.length) {
                        currentIndex = 0;
                      }
                    });
                  },
                  child: SizedBox(
                      height: 60,
                      child: Center(
                        child: Text(
                          'currentIndex=${currentIndex} ${(data[currentIndex] as Map)['ProductAdress'].toString()}',
                          style: const TextStyle(fontSize: 30),
                        ),
                      )),
                )),
                Expanded(
                    child: GestureDetector(
                  onTap: () {
                    _nrProdusController.text =
                        (data[currentIndex] as Map)['NeedCount'].toString();
                  },
                  child: SizedBox(
                    height: 60,
                    child: Center(
                      child: Text(
                        (data[currentIndex] as Map)['NeedCount'].toString(),
                        style: TextStyle(
                            fontSize: 35,
                            fontWeight: FontWeight.bold,
                            color: Colors.primaries[Random().nextInt(Colors.primaries.length)]),
                      ),
                    ),
                  ),
                )),
              ],
            ),
            // 其他组件...
          ]);
        }
    }
  },
),

内容的提问来源于stack exchange,提问作者Dima C

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 14:45:56