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

如何在Flutter中不使用DataTable,仅用Table Widget实现动态表格?

基于Flutter Table Widget实现可编辑(增删、移动)行列的表格方案

核心实现思路

通过StatefulWidget维护表格的二维数据源,结合Table、Row、Column组件动态渲染表格结构,同时为行列添加操作按钮实现增删、移动功能。所有逻辑基于Flutter原生组件,无需第三方库。

完整可运行代码示例

import 'package:flutter/material.dart';

class EditableTable extends StatefulWidget {
  const EditableTable({super.key});

  @override
  State<EditableTable> createState() => _EditableTableState();
}

class _EditableTableState extends State<EditableTable> {
  // 二维列表维护表格数据,每个元素代表单元格内容
  List<List<String>> _tableData = [
    ['单元格1-1', '单元格1-2', '单元格1-3'],
    ['单元格2-1', '单元格2-2', '单元格2-3'],
    ['单元格3-1', '单元格3-2', '单元格3-3'],
  ];

  // 添加新行
  void _addRow() {
    setState(() {
      _tableData.add(List.generate(_tableData[0].length, (index) => ''));
    });
  }

  // 删除指定行
  void _deleteRow(int index) {
    if (_tableData.length > 1) {
      setState(() {
        _tableData.removeAt(index);
      });
    }
  }

  // 向上移动行
  void _moveRowUp(int index) {
    if (index > 0) {
      setState(() {
        final temp = _tableData[index];
        _tableData[index] = _tableData[index - 1];
        _tableData[index - 1] = temp;
      });
    }
  }

  // 向下移动行
  void _moveRowDown(int index) {
    if (index < _tableData.length - 1) {
      setState(() {
        final temp = _tableData[index];
        _tableData[index] = _tableData[index + 1];
        _tableData[index + 1] = temp;
      });
    }
  }

  // 添加新列
  void _addColumn() {
    setState(() {
      for (var row in _tableData) {
        row.add('');
      }
    });
  }

  // 删除指定列
  void _deleteColumn(int index) {
    if (_tableData[0].length > 1) {
      setState(() {
        for (var row in _tableData) {
          row.removeAt(index);
        }
      });
    }
  }

  // 向左移动列
  void _moveColumnLeft(int index) {
    if (index > 0) {
      setState(() {
        for (var row in _tableData) {
          final temp = row[index];
          row[index] = row[index - 1];
          row[index - 1] = temp;
        }
      });
    }
  }

  // 向右移动列
  void _moveColumnRight(int index) {
    if (index < _tableData[0].length - 1) {
      setState(() {
        for (var row in _tableData) {
          final temp = row[index];
          row[index] = row[index + 1];
          row[index + 1] = temp;
        }
      });
    }
  }

  // 更新单元格内容
  void _updateCell(int rowIndex, int colIndex, String value) {
    setState(() {
      _tableData[rowIndex][colIndex] = value;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('可编辑表格')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            // 全局操作按钮:添加行、列
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(onPressed: _addRow, child: const Text('添加行')),
                const SizedBox(width: 16),
                ElevatedButton(onPressed: _addColumn, child: const Text('添加列')),
              ],
            ),
            const SizedBox(height: 16),
            // 表格区域,带滚动
            Expanded(
              child: SingleChildScrollView(
                scrollDirection: Axis.vertical,
                child: SingleChildScrollView(
                  scrollDirection: Axis.horizontal,
                  child: Table(
                    border: TableBorder.all(),
                    children: [
                      // 表头行(带列操作按钮)
                      TableRow(
                        children: [
                          const TableCell(child: SizedBox(width: 80)), // 占位,对应行操作列
                          ...List.generate(_tableData[0].length, (colIndex) {
                            return TableCell(
                              child: Row(
                                children: [
                                  IconButton(
                                    icon: const Icon(Icons.arrow_left),
                                    onPressed: () => _moveColumnLeft(colIndex),
                                  ),
                                  IconButton(
                                    icon: const Icon(Icons.arrow_right),
                                    onPressed: () => _moveColumnRight(colIndex),
                                  ),
                                  IconButton(
                                    icon: const Icon(Icons.delete),
                                    onPressed: () => _deleteColumn(colIndex),
                                  ),
                                  Expanded(
                                    child: TextField(
                                      decoration: const InputDecoration(border: InputBorder.none),
                                      onChanged: (value) => _updateCell(0, colIndex, value),
                                      controller: TextEditingController(text: _tableData[0][colIndex]),
                                    ),
                                  ),
                                ],
                              ),
                            );
                          }),
                        ],
                      ),
                      // 数据行(带行操作按钮)
                      ...List.generate(_tableData.length - 1, (rowIndex) {
                        final actualRowIndex = rowIndex + 1;
                        return TableRow(
                          children: [
                            // 行操作按钮组
                            TableCell(
                              child: Column(
                                children: [
                                  IconButton(
                                    icon: const Icon(Icons.arrow_upward),
                                    onPressed: () => _moveRowUp(actualRowIndex),
                                  ),
                                  IconButton(
                                    icon: const Icon(Icons.arrow_downward),
                                    onPressed: () => _moveRowDown(actualRowIndex),
                                  ),
                                  IconButton(
                                    icon: const Icon(Icons.delete),
                                    onPressed: () => _deleteRow(actualRowIndex),
                                  ),
                                ],
                              ),
                            ),
                            // 单元格内容
                            ...List.generate(_tableData[actualRowIndex].length, (colIndex) {
                              return TableCell(
                                child: TextField(
                                  decoration: const InputDecoration(border: InputBorder.none),
                                  onChanged: (value) => _updateCell(actualRowIndex, colIndex, value),
                                  controller: TextEditingController(text: _tableData[actualRowIndex][colIndex]),
                                ),
                              );
                            }),
                          ],
                        );
                      }),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// 用于运行的入口
void main() {
  runApp(const MaterialApp(home: EditableTable()));
}

关键功能解析

  1. 数据源维护
    使用List<List<String>>作为表格的核心数据源,每一层列表对应一行数据,列表中的每个元素对应单元格内容。所有增删、移动操作都是通过修改这个二维列表,再调用setState触发UI重建。

  2. 行列增删逻辑

    • 添加行:向二维列表中添加一个新的子列表,长度与现有列数一致
    • 删除行:移除指定索引的子列表,同时限制最少保留1行
    • 添加列:遍历每一行,向子列表末尾添加空字符串
    • 删除列:遍历每一行,移除指定索引的元素,限制最少保留1列
  3. 行列移动逻辑

    • 移动行:交换目标行与相邻行的位置(上移交换index和index-1,下移交换index和index+1)
    • 移动列:遍历所有行,交换目标列与相邻列的元素位置
  4. 动态表格渲染
    通过List.generate动态生成TableRow和TableCell,为表头和每一行添加对应的操作按钮,单元格使用TextField支持内容编辑,确保表格内容可修改。

注意事项

  • 代码中加入了边界判断,避免删除最后一行/列或越界移动的操作
  • 使用SingleChildScrollView包裹Table,实现表格在横竖方向的滚动,适配不同屏幕尺寸
  • 如果不需要单元格编辑,可将TextField替换为Text组件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 16:01:15