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

Flutter表格多行编辑实现求助:参考DataTables示例求简便方案

Flutter 表格多行编辑的简便实现方案

方案一:使用成熟第三方包(推荐)

最简便的方式是借助功能完善的表格组件包,比如Syncfusion Flutter DataGrid,它原生支持多行选择、批量编辑,和你提到的Datatables示例逻辑匹配:

  1. 添加依赖
    在pubspec.yaml中加入:
dependencies:
  syncfusion_flutter_datagrid: ^25.1.35 # 替换为最新版本
  1. 实现数据源与多行编辑逻辑
import 'package:syncfusion_flutter_datagrid/datagrid.dart';

// 定义数据模型
class Employee {
  Employee(this.id, this.name, this.designation, this.salary);

  final int id;
  String name;
  String designation;
  double salary;
}

// 自定义数据源
class EmployeeDataSource extends DataGridSource {
  EmployeeDataSource(List<Employee> employees) {
    _employees = employees
        .map<DataGridRow>((e) => DataGridRow(cells: [
              DataGridCell<int>(columnName: 'id', value: e.id),
              DataGridCell<String>(columnName: 'name', value: e.name),
              DataGridCell<String>(columnName: 'designation', value: e.designation),
              DataGridCell<double>(columnName: 'salary', value: e.salary),
            ]))
        .toList();
  }

  List<DataGridRow> _employees = [];
  List<DataGridRow> selectedRows = [];

  @override
  List<DataGridRow> get rows => _employees;

  @override
  DataGridRowAdapter buildRow(DataGridRow row) {
    return DataGridRowAdapter(
        cells: row.getCells().map<Widget>((dataGridCell) {
      return Container(
        alignment: Alignment.center,
        padding: const EdgeInsets.all(8.0),
        child: Text(dataGridCell.value.toString()),
      );
    }).toList());
  }
}

// 页面实现
class MultiRowEditTable extends StatefulWidget {
  const MultiRowEditTable({super.key});

  @override
  State<MultiRowEditTable> createState() => _MultiRowEditTableState();
}

class _MultiRowEditTableState extends State<MultiRowEditTable> {
  late EmployeeDataSource _dataSource;
  final List<Employee> _employees = [
    Employee(1, 'John Doe', 'Software Engineer', 80000),
    Employee(2, 'Jane Smith', 'Product Manager', 95000),
    Employee(3, 'Bob Brown', 'Designer', 75000),
  ];

  @override
  void initState() {
    super.initState();
    _dataSource = EmployeeDataSource(_employees);
  }

  // 批量编辑弹窗
  void _showBatchEditDialog() {
    if (_dataSource.selectedRows.isEmpty) return;

    String newDesignation = '';
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('批量修改职位'),
        content: TextField(
          onChanged: (value) => newDesignation = value,
          decoration: const InputDecoration(hintText: '输入新职位'),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('取消'),
          ),
          TextButton(
            onPressed: () {
              // 更新选中行数据
              for (var row in _dataSource.selectedRows) {
                final employee = _employees.firstWhere(
                    (e) => e.id == row.getCells()[0].value);
                employee.designation = newDesignation;
              }
              _dataSource.buildDataGridRows();
              _dataSource.notifyListeners();
              Navigator.pop(context);
            },
            child: const Text('确认'),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('多行编辑表格')),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: _showBatchEditDialog,
            child: const Text('批量编辑选中行'),
          ),
          Expanded(
            child: SfDataGrid(
              source: _dataSource,
              selectionMode: SelectionMode.multiple,
              onSelectionChanged: (addedRows, removedRows) {
                setState(() {
                  _dataSource.selectedRows = addedRows;
                });
              },
              columns: <GridColumn>[
                GridColumn(
                    columnName: 'id',
                    label: Container(
                        padding: const EdgeInsets.all(8.0),
                        alignment: Alignment.center,
                        child: const Text('ID'))),
                GridColumn(
                    columnName: 'name',
                    label: Container(
                        padding: const EdgeInsets.all(8.0),
                        alignment: Alignment.center,
                        child: const Text('姓名'))),
                GridColumn(
                    columnName: 'designation',
                    label: Container(
                        padding: const EdgeInsets.all(8.0),
                        alignment: Alignment.center,
                        child: const Text('职位'))),
                GridColumn(
                    columnName: 'salary',
                    label: Container(
                        padding: const EdgeInsets.all(8.0),
                        alignment: Alignment.center,
                        child: const Text('薪资'))),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

方案二:原生DataTable自定义实现

如果不想引入第三方包,可以用Flutter原生DataTable组件手动实现:

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

  @override
  State<CustomMultiRowEditTable> createState() => _CustomMultiRowEditTableState();
}

class _CustomMultiRowEditTableState extends State<CustomMultiRowEditTable> {
  List<Employee> _employees = [
    Employee(1, 'John Doe', 'Software Engineer', 80000),
    Employee(2, 'Jane Smith', 'Product Manager', 95000),
    Employee(3, 'Bob Brown', 'Designer', 75000),
  ];
  List<bool> _selected = [];

  @override
  void initState() {
    super.initState();
    _selected = List.generate(_employees.length, (_) => false);
  }

  void _showBatchEdit() {
    final selectedEmployees = _employees
        .asMap()
        .entries
        .where((entry) => _selected[entry.key])
        .map((entry) => entry.value)
        .toList();
    if (selectedEmployees.isEmpty) return;

    String newSalary = '';
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('批量修改薪资'),
        content: TextField(
          keyboardType: TextInputType.number,
          onChanged: (value) => newSalary = value,
          decoration: const InputDecoration(hintText: '输入新薪资'),
        ),
        actions: [
          TextButton(onPressed: () => Navigator.pop(context), child: const Text('取消')),
          TextButton(
            onPressed: () {
              final salary = double.tryParse(newSalary);
              if (salary != null) {
                for (var emp in selectedEmployees) {
                  emp.salary = salary;
                }
                setState(() {});
              }
              Navigator.pop(context);
            },
            child: const Text('确认'),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('自定义多行编辑表格')),
      body: Column(
        children: [
          ElevatedButton(onPressed: _showBatchEdit, child: const Text('批量编辑')),
          Expanded(
            child: SingleChildScrollView(
              child: DataTable(
                columns: const [
                  DataColumn(label: Text('选择')),
                  DataColumn(label: Text('ID')),
                  DataColumn(label: Text('姓名')),
                  DataColumn(label: Text('职位')),
                  DataColumn(label: Text('薪资')),
                ],
                rows: _employees.asMap().entries.map((entry) {
                  final index = entry.key;
                  final emp = entry.value;
                  return DataRow(
                    selected: _selected[index],
                    onSelectChanged: (value) {
                      setState(() {
                        _selected[index] = value ?? false;
                      });
                    },
                    cells: [
                      DataCell(Checkbox(
                        value: _selected[index],
                        onChanged: (value) {
                          setState(() {
                            _selected[index] = value ?? false;
                          });
                        },
                      )),
                      DataCell(Text(emp.id.toString())),
                      DataCell(Text(emp.name)),
                      DataCell(Text(emp.designation)),
                      DataCell(Text(emp.salary.toString())),
                    ],
                  );
                }).toList(),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

两种方案里,第三方包的方式更省心,自带多选、状态管理等功能;原生自定义则更轻量化,适合简单场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 21:33:25