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

Flutter实现固定表头+可滚动内容表格(不使用第三方库)

Flutter实现固定表头、内容自适应滚动的Table(无第三方库)

问题分析

用Expanded包裹导致卡顿,本质是未对内容Table的高度做有效约束,同时大量直接生成的TableRow会让Widget树过于庞大,增加布局计算压力;硬编码高度又无法适配不同屏幕尺寸。

解决方案

通过调整Widget结构,让内容滚动区域自适应剩余空间,并优化列表渲染性能:

1. 核心结构调整

将第二个Obx内的Column保留,给内容区域的SingleChildScrollView套上Expanded,让它自动占据父Column的剩余可用空间,同时确保表头和内容Table的列宽完全一致,避免错位:

Obx(() {
  return Column(
    children: [
      // 固定表头Table
      Table(
        columnWidths: const {
          0: FixedColumnWidth(100),
          1: FlexColumnWidth(2),
          2: FlexColumnWidth(1),
        },
        border: TableBorder.all(),
        children: const [
          TableRow(
            children: [
              Padding(padding: EdgeInsets.all(8), child: Text('ID')),
              Padding(padding: EdgeInsets.all(8), child: Text('名称')),
              Padding(padding: EdgeInsets.all(8), child: Text('状态')),
            ],
          ),
        ],
      ),
      const SizedBox(height: 8),
      // 自适应高度的可滚动内容区域
      Expanded(
        child: SingleChildScrollView(
          physics: const AlwaysScrollableScrollPhysics(),
          child: Table(
            // 必须和表头保持完全一致的列宽配置
            columnWidths: const {
              0: FixedColumnWidth(100),
              1: FlexColumnWidth(2),
              2: FlexColumnWidth(1),
            },
            border: TableBorder.all(),
            children: List.generate(50, (index) {
              return TableRow(
                children: [
                  Padding(padding: EdgeInsets.all(8), child: Text('$index')),
                  Padding(padding: EdgeInsets.all(8), child: Text('测试数据$index')),
                  Padding(padding: EdgeInsets.all(8), child: Text(index % 2 == 0 ? '正常' : '异常')),
                ],
              );
            }),
          ),
        ),
      ),
    ],
  );
})

2. 解决卡顿的关键优化

如果内容数据量较大(比如超过50行),直接生成所有TableRow会导致Widget树节点过多,引发卡顿。此时可以用ListView.builder模拟Table结构,实现懒加载渲染:

// 表头部分保持不变
Table(/* 表头配置 */),
const SizedBox(height: 8),
// 优化后的可滚动内容区域
Expanded(
  child: ListView.builder(
    itemCount: 1000, // 大量数据
    itemBuilder: (context, index) {
      return Row(
        children: [
          // 对应表头第一列,宽度一致
          Container(
            width: 100,
            padding: const EdgeInsets.all(8),
            decoration: const BoxDecoration(
              border: Border(bottom: BorderSide(), right: BorderSide()),
            ),
            child: Text('$index'),
          ),
          // 对应表头第二列,flex比例一致
          Expanded(
            flex: 2,
            child: Container(
              padding: const EdgeInsets.all(8),
              decoration: const BoxDecoration(
                border: Border(bottom: BorderSide(), right: BorderSide()),
              ),
              child: Text('测试数据$index'),
            ),
          ),
          // 对应表头第三列,flex比例一致
          Expanded(
            flex: 1,
            child: Container(
              padding: const EdgeInsets.all(8),
              decoration: const BoxDecoration(
                border: Border(bottom: BorderSide()),
              ),
              child: Text(index % 2 == 0 ? '正常' : '异常'),
            ),
          ),
        ],
      );
    },
  ),
)

关键注意事项

  • 列宽对齐:表头和内容的列宽(或flex比例)必须严格匹配,否则会出现视觉错位。
  • 自适应高度:Expanded必须放在Column内,且Column的父容器要有确定的高度约束(比如Scaffold的body本身是有高度约束的)。
  • 性能优化:数据量大时,优先用ListView.builder懒加载,避免一次性生成大量Widget。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 23:55:19