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

如何在Flutter中实现行列交叉的表格?求技术实现方案

Flutter 行列交叉表格实现方案

基础静态行列交叉表格

如果是数据量较小的静态场景,直接使用Flutter内置的Table组件即可实现行列交叉效果,核心是将第一行和第一列作为交叉标题。

示例代码:

class CrossTable extends StatelessWidget {
  // 模拟交叉数据:行标题、列标题、单元格内容
  final List<String> rowTitles = ['行1', '行2', '行3', '行4'];
  final List<String> columnTitles = ['列1', '列2', '列3', '列4'];
  final List<List<String>> data = [
    ['1-1', '1-2', '1-3', '1-4'],
    ['2-1', '2-2', '2-3', '2-4'],
    ['3-1', '3-2', '3-3', '3-4'],
    ['4-1', '4-2', '4-3', '4-4'],
  ];

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      scrollDirection: Axis.vertical,
      child: SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: Table(
          border: TableBorder.all(color: Colors.grey),
          children: [
            // 首行:列标题(含左上角空白单元格)
            TableRow(
              decoration: BoxDecoration(color: Colors.grey[200]),
              children: [
                Container(width: 80, height: 40, alignment: Alignment.center),
                ...columnTitles.map((title) => Container(
                      width: 120,
                      height: 40,
                      alignment: Alignment.center,
                      child: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
                    )),
              ],
            ),
            // 数据行:行标题+内容单元格
            ...rowTitles.asMap().entries.map((entry) {
              final rowIndex = entry.key;
              final rowTitle = entry.value;
              return TableRow(
                children: [
                  Container(
                    width: 80,
                    height: 40,
                    alignment: Alignment.center,
                    decoration: BoxDecoration(color: Colors.grey[200]),
                    child: Text(rowTitle, style: const TextStyle(fontWeight: FontWeight.bold)),
                  ),
                  ...data[rowIndex].map((cell) => Container(
                        width: 120,
                        height: 40,
                        alignment: Alignment.center,
                        child: Text(cell),
                      )),
                ],
              );
            }),
          ],
        ),
      ),
    );
  }
}

带固定表头的行列交叉表格(滚动时表头锁定)

如果数据量较大,需要滚动时保持行标题和列标题固定,可通过Stack结合SingleChildScrollView实现,同时用滚动控制器同步横向/纵向滚动,保证表头与内容对齐。

示例代码:

class FixedCrossTable extends StatefulWidget {
  @override
  _FixedCrossTableState createState() => _FixedCrossTableState();
}

class _FixedCrossTableState extends State<FixedCrossTable> {
  // 模拟大量数据
  final List<String> rowTitles = List.generate(20, (index) => '行${index+1}');
  final List<String> columnTitles = List.generate(15, (index) => '列${index+1}');
  final List<List<String>> data = List.generate(20, (row) => List.generate(15, (col) => '${row+1}-${col+1}'));

  // 同步滚动控制器
  final ScrollController _verticalController = ScrollController();
  final ScrollController _horizontalController = ScrollController();

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        // 主内容区:可滚动的数据表格
        SingleChildScrollView(
          controller: _verticalController,
          scrollDirection: Axis.vertical,
          child: SingleChildScrollView(
            controller: _horizontalController,
            scrollDirection: Axis.horizontal,
            child: Table(
              border: TableBorder.all(color: Colors.grey),
              children: [
                // 占位首行(被顶部固定表头覆盖)
                TableRow(
                  children: [
                    const SizedBox(width: 80, height: 40),
                    ...columnTitles.map((_) => const SizedBox(width: 120, height: 40)),
                  ],
                ),
                // 数据行(左侧被固定行标题覆盖)
                ...rowTitles.asMap().entries.map((entry) {
                  final rowIndex = entry.key;
                  return TableRow(
                    children: [
                      const SizedBox(width: 80, height: 40),
                      ...data[rowIndex].map((cell) => Container(
                            width: 120,
                            height: 40,
                            alignment: Alignment.center,
                            child: Text(cell),
                          )),
                    ],
                  );
                }),
              ],
            ),
          ),
        ),
        // 顶部固定列标题
        SingleChildScrollView(
          controller: _horizontalController,
          scrollDirection: Axis.horizontal,
          child: Container(
            color: Colors.white,
            child: Table(
              border: TableBorder.all(color: Colors.grey),
              children: [
                TableRow(
                  decoration: BoxDecoration(color: Colors.grey[200]),
                  children: [
                    const SizedBox(width: 80, height: 40), // 左上角空白
                    ...columnTitles.map((title) => Container(
                          width: 120,
                          height: 40,
                          alignment: Alignment.center,
                          child: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
                        )),
                  ],
                ),
              ],
            ),
          ),
        ),
        // 左侧固定行标题
        SingleChildScrollView(
          controller: _verticalController,
          scrollDirection: Axis.vertical,
          child: Container(
            color: Colors.white,
            child: Table(
              border: TableBorder.all(color: Colors.grey),
              children: [
                // 左上角空白单元格
                TableRow(
                  decoration: BoxDecoration(color: Colors.grey[200]),
                  children: [const SizedBox(width: 80, height: 40)],
                ),
                // 行标题列表
                ...rowTitles.map((title) => TableRow(
                      decoration: BoxDecoration(color: Colors.grey[200]),
                      children: [
                        Container(
                          width: 80,
                          height: 40,
                          alignment: Alignment.center,
                          child: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
                        ),
                      ],
                    )),
              ],
            ),
          ),
        ),
      ],
    );
  }
}

自定义调整建议

  • 可根据需求修改单元格的width、height、背景色、字体样式,强化表头与内容的区分。
  • 如果需要动态加载数据,只需将示例中的静态数据替换为接口返回的数据源即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 18:45:49