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

Flutter DataTable结合FutureBuilder红屏列数不匹配报错修复

Flutter DataTable 异步加载行数据红屏报错修复

报错根因

抛出的Failed assertion: '!rows.any((DataRow row) => row.cells.length != columns.length)'断言触发逻辑非常明确:DataTable 组件强制要求每一行的 DataCell 数量,必须和表头 DataColumn 的数量完全相等,只要任意一行单元格数多了或者少了,就会直接抛红屏异常。
结合用 FutureBuilder 异步加载List<MaintenanceDetailsByDriverNo>生成 rows 的场景,常见触发点共四类:

  • 异步加载的不同状态分支(加载中/报错/空数据/加载完成)返回的行单元格数和表头列数不匹配,最常见的是加载中只放了一个加载动画占1个单元格,和多列的表头对不上
  • 遍历业务数据生成 DataRow 时,加了字段判空的条件渲染,某条数据字段为空时直接跳过了对应单元格的生成,导致这一行单元格数少了
  • 调整表头列数的时候,忘了同步修改行单元格的生成逻辑,两边数量不对等
  • 业务列表里存在脏数据,某条MaintenanceDetailsByDriverNo对象字段缺失,导致条件渲染时少生成了单元格

修复步骤

1. 先抽离固定列数,从根源避免硬编码不一致

不要分别在columns和rows的代码里硬编码列数,抽成统一常量,两边都用这个值做生成基准:

// 统一列数常量,所有行、列生成都以这个值为准
const int _tableColumnNum = 5;

List<DataColumn> _generateColumns() {
  return List.generate(_tableColumnNum, (colIndex) {
    switch (colIndex) {
      case 0: return const DataColumn(label: Text('维保单号'));
      case 1: return const DataColumn(label: Text('绑定车牌'));
      case 2: return const DataColumn(label: Text('维保日期'));
      case 3: return const DataColumn(label: Text('维保费用'));
      case 4: return const DataColumn(label: Text('操作'));
      default: return const DataColumn(label: SizedBox.shrink());
    }
  });
}

2. 统一所有异步状态的行生成逻辑

FutureBuilder 的所有状态分支(等待/报错/空数据/正常加载),返回的每一行 DataRow 的 cells 长度必须严格等于_tableColumnNum,举个典型错误和对应修正:

❌ 错误写法:加载态行只有1个单元格,和5列表头不匹配

if (snapshot.connectionState == ConnectionState.waiting) {
  return DataTable(
    columns: _generateColumns(),
    rows: [
      // 这里cells长度为1,直接触发断言
      DataRow(cells: [const DataCell(Center(child: CircularProgressIndicator()))])
    ],
  );
}

✅ 正确写法:占位行单元格数和表头完全对齐

if (snapshot.connectionState == ConnectionState.waiting) {
  return DataTable(
    columns: _generateColumns(),
    rows: [
      DataRow(
        cells: List.generate(_tableColumnNum, (colIndex) {
          // 加载提示放在第一列,其余列留空占位
          if (colIndex == 0) {
            return const DataCell(Center(child: CircularProgressIndicator()));
          }
          return const DataCell(SizedBox.shrink());
        }),
      )
    ],
  );
}

空数据、加载报错的占位行也按这个逻辑写,不要只放1个占满整行的单元格——DataTable 本身不支持单元格跨列,硬写就会触发数量不匹配的断言。

3. 业务数据行用统一规则生成

遍历List<MaintenanceDetailsByDriverNo>生成正式数据行时,同样用List.generate按固定列数生成,不要手动逐个写DataCell,避免漏写:

List<DataRow> _generateDataRows(List<MaintenanceDetailsByDriverNo> sourceList) {
  return sourceList.map((item) {
    return DataRow(
      cells: List.generate(_tableColumnNum, (colIndex) {
        switch (colIndex) {
          case 0: return DataCell(Text(item.maintainOrderNo ?? '-'));
          case 1: return DataCell(Text(item.plateNumber ?? '-'));
          case 2: return DataCell(Text(item.maintainDate ?? '-'));
          case 3: return DataCell(Text('${item.cost ?? 0} 元'));
          case 4: return DataCell(TextButton(onPressed: (){}, child: const Text('查看详情')));
          default: return const DataCell(SizedBox.shrink());
        }
      }),
    );
  }).toList();
}

所有字段判空给默认值,不要因为字段为空就跳过单元格生成。

快速定位异常行技巧

如果改完还是报错,不用逐行翻代码找哪行数量不对,在把rows传给DataTable之前加一段校验代码,控制台会直接打印异常行的位置:

final columnLength = _generateColumns().length;
for (int rowIndex = 0; rowIndex < tableRows.length; rowIndex++) {
  final cellLength = tableRows[rowIndex].cells.length;
  if (cellLength != columnLength) {
    debugPrint('异常行位置:第$rowIndex行,单元格数:$cellLength,表头列数:$columnLength');
  }
}

拿到异常行索引之后,直接对应到列表里的数据项或者占位行逻辑,即可快速定位问题。

内容的提问来源于stack exchange,提问作者Faith.youssef.abk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 21:06:23