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

Flutter空值检查与RangeError问题求助:Widget渲染失败

问题排查与修复方案

错误原因分析

1. "null check operator was used on a null value"错误

源自外层StreamBuilder中的userData!.length:尽管snapshot.hasData为true,但read('${widget.expChoice.toString()} Sales')返回的List<showSummaryReport>可能为空(或异步状态切换时临时出现null),非空断言!强制解包null值导致崩溃。

2. RangeError(index)错误

来自内层StreamBuilder的searched = searchedData[index]:

  • 内层read('${test} Sales')返回的数组长度可能小于外层ListView的index值(比如外层有2个元素,内层仅1个),直接用外层index访问内层数组会触发越界;
  • 内层StreamBuilder未处理ConnectionState.waiting和hasError状态,数据未加载完成时就直接访问空数组,引发越界错误。

修复代码示例

Visibility(
  visible: gotChoice,
  child: StreamBuilder<List<showSummaryReport>>(
    stream: read('${widget.expChoice.toString()} Sales'),
    builder: (context, snapshot) {
      // 处理加载中状态
      if (snapshot.connectionState == ConnectionState.waiting) {
        return const Center(child: CircularProgressIndicator());
      }
      // 处理错误状态
      if (snapshot.hasError) {
        return const Center(child: Text("加载数据失败"));
      }
      // 安全获取数据,避免null
      final userData = snapshot.data ?? [];
      // 数据为空时显示提示
      if (userData.isEmpty) {
        return const Center(child: Text("暂无数据"));
      }
      return Expanded(
        child: ListView.builder(
          itemCount: userData.length,
          itemBuilder: (context, index) {
            final service = userData[index];
            return StreamBuilder<List<showSummaryReport>>(
              stream: read('${test} Sales'),
              builder: (context, innerSnapshot) {
                // 内层加载中状态
                if (innerSnapshot.connectionState == ConnectionState.waiting) {
                  return const Center(child: CircularProgressIndicator());
                }
                // 内层错误状态
                if (innerSnapshot.hasError) {
                  return const Center(child: Text("对比数据加载失败"));
                }
                // 安全获取内层数据
                final searchedData = innerSnapshot.data ?? [];
                // 通过serviceName匹配对应数据,而非依赖index
                final searched = searchedData.firstWhere(
                  (item) => item.serviceName == service.serviceName,
                  // 找不到匹配项时返回默认值(需确保showSummaryReport有默认构造)
                  orElse: () => showSummaryReport(serviceName: "无数据", sales: 0),
                );
                return Column(
                  children: [
                    ListTile(
                      onTap: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) => selectedDetailReport(
                              detail: '${service.serviceName} Sales',
                              month: widget.expChoice.toString(),
                            ),
                          ),
                        );
                      },
                      title: Card(
                        color: 'CAF0F8'.toColor(),
                        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
                        child: Table(
                          children: [
                            const TableRow(children: [SizedBox(height: 10), SizedBox(height: 10)]),
                            TableRow(
                              children: [
                                Padding(
                                  padding: const EdgeInsets.fromLTRB(30, 0, 0, 0),
                                  child: Text('服务名称:', style: TextStyle(fontFamily: 'MonSemi', fontSize: 17)),
                                ),
                                Center(child: Text(service.serviceName.toString(), style: TextStyle(fontFamily: 'MonSemi', fontSize: 17))),
                              ],
                            ),
                            const TableRow(children: [SizedBox(height: 10), SizedBox(height: 10)]),
                            TableRow(
                              children: [
                                Padding(
                                  padding: const EdgeInsets.fromLTRB(30, 0, 0, 0),
                                  child: Text('当月销售额:', style: TextStyle(fontFamily: 'MonSemi', fontSize: 17)),
                                ),
                                Center(child: Text(service.sales.toString(), style: TextStyle(fontFamily: 'MonSemi', fontSize: 17))),
                              ],
                            ),
                            const TableRow(children: [SizedBox(height: 10), SizedBox(height: 10)]),
                            TableRow(
                              children: [
                                Padding(
                                  padding: const EdgeInsets.fromLTRB(30, 0, 0, 0),
                                  child: Text('${test} 销售额:', style: TextStyle(fontFamily: 'MonSemi', fontSize: 17)),
                                ),
                                Center(child: Text('${searched.sales}', style: TextStyle(fontFamily: 'MonSemi', fontSize: 17))),
                              ],
                            ),
                            const TableRow(children: [SizedBox(height: 10), SizedBox(height: 10)]),
                            TableRow(
                              children: [
                                Padding(
                                  padding: const EdgeInsets.fromLTRB(30, 0, 0, 0),
                                  child: Text('${test} 销售额:', style: TextStyle(fontFamily: 'MonSemi', fontSize: 17)),
                                ),
                                Center(child: Text('${searched.sales}', style: TextStyle(fontFamily: 'MonSemi', fontSize: 17))),
                              ],
                            ),
                            const TableRow(children: [SizedBox(height: 10), SizedBox(height: 10)]),
                          ],
                        ),
                      ),
                    ),
                  ],
                );
              },
            );
          },
        ),
      );
    },
  ),
);

关键修复点说明

  • 替换非空断言!为?? [],确保数据始终为非空数组;
  • 增加空数据判断,避免空列表调用ListView.builder;
  • 内层StreamBuilder补全加载中、错误状态处理;
  • 用firstWhere通过serviceName匹配对应数据,彻底消除index越界风险;
  • 为firstWhere设置orElse默认值,处理找不到匹配项的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 17:15:35