基础入门 Flutter for OpenHarmony:DataTable 数据表格组件详解

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
🎯 欢迎来到 Flutter for OpenHarmony 社区!本文将深入讲解 Flutter 中 DataTable 数据表格组件的使用方法,带你从基础到精通,掌握数据表格的排序、选择、分页等高级功能。


一、DataTable 组件概述

在移动应用开发中,数据表格是一种常见的数据管理方式。Flutter 提供了 DataTable 组件,专门用于展示和管理结构化数据。与 Table 组件不同,DataTable 提供了排序、选择、分页等高级功能,适合需要数据交互的场景。

📋 DataTable 组件特点

特点 说明
列排序 支持点击列标题进行排序
行选择 支持单选和多选
分页功能 支持数据分页显示
自定义列 支持自定义列宽、对齐方式、排序状态
空数据处理 支持自定义空数据提示
Material 设计 遵循 Material Design 设计规范

DataTable 与 Table 的区别

特性 Table DataTable
排序功能 不支持 支持
行选择 不支持 支持
分页功能 不支持 需配合 PaginatedDataTable
自定义灵活性 较低
代码复杂度 较低 较高
适用场景 简单表格展示 数据管理、后台系统

💡 使用场景:DataTable 适合需要排序、选择功能的数据表格,如用户管理、订单列表、商品管理等。如果只需要简单的表格展示,建议使用 Table 组件。


二、DataTable 基础用法

DataTable 的使用需要定义列和数据行。让我们从最基础的用法开始学习。

2.1 最简单的 DataTable

最基础的 DataTable 需要设置 columnsrows 参数:

dart 复制代码
DataTable(
  columns: const [
    DataColumn(label: Text('姓名')),
    DataColumn(label: Text('年龄')),
    DataColumn(label: Text('城市')),
  ],
  rows: const [
    DataRow(cells: [
      DataCell(Text('张三')),
      DataCell(Text('25')),
      DataCell(Text('北京')),
    ]),
    DataRow(cells: [
      DataCell(Text('李四')),
      DataCell(Text('30')),
      DataCell(Text('上海')),
    ]),
  ],
)

代码解析:

  • columns:表格列定义,是一个 DataColumn 列表
  • rows:表格行数据,是一个 DataRow 列表
  • DataColumn:定义列的标题和属性
  • DataRow:定义一行的数据
  • DataCell:定义单元格的数据

2.2 设置列宽和对齐

通过 DataColumn 的属性设置列宽和对齐方式:

dart 复制代码
DataTable(
  columns: const [
    DataColumn(
      label: Text('姓名'),
      numeric: false,
    ),
    DataColumn(
      label: Text('年龄'),
      numeric: true,
    ),
    DataColumn(
      label: Text('城市'),
      numeric: false,
    ),
  ],
  rows: const [
    DataRow(cells: [
      DataCell(Text('张三')),
      DataCell(Text('25')),
      DataCell(Text('北京')),
    ]),
  ],
)

DataColumn 参数详解:

参数 说明
label 列标题,通常使用 Text 组件
numeric 是否为数值列,影响对齐方式
tooltip 长按列标题时显示的提示
onSort 排序回调

2.3 完整示例

下面是一个完整的可运行示例,展示了 DataTable 的基础用法:

dart 复制代码
class DataTableBasicExample extends StatelessWidget {
  const DataTableBasicExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('DataTable 基础示例')),
      body: SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: DataTable(
          headingRowColor: WidgetStateProperty.all(Colors.blue[100]),
          dataRowColor: WidgetStateProperty.all(Colors.white),
          headingTextStyle: const TextStyle(
            fontWeight: FontWeight.bold,
            color: Colors.black87,
          ),
          columns: const [
            DataColumn(label: Text('ID')),
            DataColumn(label: Text('姓名')),
            DataColumn(label: Text('年龄'), numeric: true),
            DataColumn(label: Text('城市')),
            DataColumn(label: Text('职业')),
          ],
          rows: const [
            DataRow(cells: [
              DataCell(Text('001')),
              DataCell(Text('张三')),
              DataCell(Text('25')),
              DataCell(Text('北京')),
              DataCell(Text('工程师')),
            ]),
            DataRow(cells: [
              DataCell(Text('002')),
              DataCell(Text('李四')),
              DataCell(Text('30')),
              DataCell(Text('上海')),
              DataCell(Text('设计师')),
            ]),
            DataRow(cells: [
              DataCell(Text('003')),
              DataCell(Text('王五')),
              DataCell(Text('28')),
              DataCell(Text('广州')),
              DataCell(Text('产品经理')),
            ]),
          ],
        ),
      ),
    );
  }
}

三、DataColumn 列定义详解

DataColumn 用于定义表格的列,包括标题、排序、对齐等属性。

3.1 基本用法

dart 复制代码
DataColumn(
  label: Text('姓名'),
  numeric: false,
  tooltip: '用户的姓名',
)

3.2 排序功能

通过 onSort 回调实现列排序:

dart 复制代码
class SortableDataTable extends StatefulWidget {
  const SortableDataTable({super.key});

  @override
  State<SortableDataTable> createState() => _SortableDataTableState();
}

class _SortableDataTableState extends State<SortableDataTable> {
  final List<User> _users = [
    User(id: '001', name: '张三', age: 25, city: '北京'),
    User(id: '002', name: '李四', age: 30, city: '上海'),
    User(id: '003', name: '王五', age: 28, city: '广州'),
    User(id: '004', name: '赵六', age: 22, city: '深圳'),
  ];

  bool _sortAscending = true;
  int _sortColumnIndex = 0;

  void _sortData(int columnIndex, bool ascending) {
    setState(() {
      _sortColumnIndex = columnIndex;
      _sortAscending = ascending;
      _users.sort((a, b) {
        int result;
        switch (columnIndex) {
          case 0:
            result = a.id.compareTo(b.id);
            break;
          case 1:
            result = a.name.compareTo(b.name);
            break;
          case 2:
            result = a.age.compareTo(b.age);
            break;
          case 3:
            result = a.city.compareTo(b.city);
            break;
          default:
            result = 0;
        }
        return ascending ? result : -result;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('排序功能')),
      body: SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: DataTable(
          sortColumnIndex: _sortColumnIndex,
          sortAscending: _sortAscending,
          columns: [
            DataColumn(
              label: const Text('ID'),
              onSort: (columnIndex, ascending) => _sortData(columnIndex, ascending),
            ),
            DataColumn(
              label: const Text('姓名'),
              onSort: (columnIndex, ascending) => _sortData(columnIndex, ascending),
            ),
            DataColumn(
              label: const Text('年龄'),
              numeric: true,
              onSort: (columnIndex, ascending) => _sortData(columnIndex, ascending),
            ),
            DataColumn(
              label: const Text('城市'),
              onSort: (columnIndex, ascending) => _sortData(columnIndex, ascending),
            ),
          ],
          rows: _users.map((user) => DataRow(cells: [
            DataCell(Text(user.id)),
            DataCell(Text(user.name)),
            DataCell(Text('${user.age}')),
            DataCell(Text(user.city)),
          ])).toList(),
        ),
      ),
    );
  }
}

class User {
  final String id;
  final String name;
  final int age;
  final String city;

  User({required this.id, required this.name, required this.age, required this.city});
}

📊 DataColumn 属性速查表

属性 类型 说明
label Widget 列标题
numeric bool 是否为数值列
tooltip String? 提示文本
onSort void Function(int, bool)? 排序回调

四、DataRow 行定义详解

DataRow 用于定义表格的一行数据,支持选择、状态等功能。

4.1 基本用法

dart 复制代码
DataRow(
  cells: [
    DataCell(Text('张三')),
    DataCell(Text('25')),
    DataCell(Text('北京')),
  ],
)

4.2 行选择功能

通过 selectedonSelectChanged 实现行选择:

dart 复制代码
class SelectableDataTable extends StatefulWidget {
  const SelectableDataTable({super.key});

  @override
  State<SelectableDataTable> createState() => _SelectableDataTableState();
}

class _SelectableDataTableState extends State<SelectableDataTable> {
  final List<User> _users = [
    User(id: '001', name: '张三', age: 25, city: '北京'),
    User(id: '002', name: '李四', age: 30, city: '上海'),
    User(id: '003', name: '王五', age: 28, city: '广州'),
  ];

  final Set<String> _selectedIds = {};

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('行选择'),
        actions: [
          if (_selectedIds.isNotEmpty)
            TextButton(
              onPressed: () {
                setState(() {
                  _users.removeWhere((user) => _selectedIds.contains(user.id));
                  _selectedIds.clear();
                });
              },
              child: Text('删除 (${_selectedIds.length})'),
            ),
        ],
      ),
      body: SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: DataTable(
          columns: const [
            DataColumn(label: Text('ID')),
            DataColumn(label: Text('姓名')),
            DataColumn(label: Text('年龄')),
            DataColumn(label: Text('城市')),
          ],
          rows: _users.map((user) => DataRow(
            selected: _selectedIds.contains(user.id),
            onSelectChanged: (selected) {
              setState(() {
                if (selected == true) {
                  _selectedIds.add(user.id);
                } else {
                  _selectedIds.remove(user.id);
                }
              });
            },
            cells: [
              DataCell(Text(user.id)),
              DataCell(Text(user.name)),
              DataCell(Text('${user.age}')),
              DataCell(Text(user.city)),
            ],
          )).toList(),
        ),
      ),
    );
  }
}

4.3 行状态

DataRow 支持不同的状态:

dart 复制代码
DataRow(
  selected: true,
  onSelectChanged: (selected) {},
  color: WidgetStateProperty.resolveWith((states) {
    if (states.contains(WidgetState.selected)) {
      return Colors.blue.withOpacity(0.1);
    }
    return null;
  }),
  cells: [...],
)

📊 DataRow 属性速查表

属性 类型 说明
cells List 单元格列表
selected bool 是否选中
onSelectChanged void Function(bool?)? 选择状态变化回调
color WidgetStateProperty<Color?>? 行颜色
onLongPress VoidCallback? 长按回调

五、DataCell 单元格详解

DataCell 用于定义表格的单元格,支持编辑、点击等交互。

5.1 基本用法

dart 复制代码
DataCell(Text('张三'))

5.2 可编辑单元格

通过 showEditIcononTap 实现可编辑效果:

dart 复制代码
DataCell(
  Text('张三'),
  showEditIcon: true,
  onTap: () {
    // 处理编辑操作
  },
)

5.3 占位符单元格

当数据为空时显示占位符:

dart 复制代码
DataCell(
  Text(''),
  placeholder: true,
)

📊 DataCell 属性速查表

属性 类型 说明
child Widget 单元格内容
placeholder bool 是否为占位符
showEditIcon bool 是否显示编辑图标
onTap VoidCallback? 点击回调
onLongPress VoidCallback? 长按回调
onDoubleTap VoidCallback? 双击回调
onTapDown GestureTapDownCallback? 按下回调
onTapCancel VoidCallback? 取消点击回调

六、DataTable 实际应用场景

DataTable 在实际开发中有着广泛的应用,让我们通过具体示例来学习。

6.1 用户管理表格

dart 复制代码
class UserManagementTable extends StatefulWidget {
  const UserManagementTable({super.key});

  @override
  State<UserManagementTable> createState() => _UserManagementTableState();
}

class _UserManagementTableState extends State<UserManagementTable> {
  final List<User> _users = [
    User(id: '001', name: '张三', age: 25, city: '北京', status: '活跃'),
    User(id: '002', name: '李四', age: 30, city: '上海', status: '离线'),
    User(id: '003', name: '王五', age: 28, city: '广州', status: '活跃'),
    User(id: '004', name: '赵六', age: 22, city: '深圳', status: '离线'),
  ];

  final Set<String> _selectedIds = {};
  bool _sortAscending = true;
  int _sortColumnIndex = 0;

  void _sortData(int columnIndex, bool ascending) {
    setState(() {
      _sortColumnIndex = columnIndex;
      _sortAscending = ascending;
      _users.sort((a, b) {
        int result;
        switch (columnIndex) {
          case 0:
            result = a.id.compareTo(b.id);
            break;
          case 1:
            result = a.name.compareTo(b.name);
            break;
          case 2:
            result = a.age.compareTo(b.age);
            break;
          case 3:
            result = a.city.compareTo(b.city);
            break;
          default:
            result = 0;
        }
        return ascending ? result : -result;
      });
    });
  }

  void _deleteSelected() {
    setState(() {
      _users.removeWhere((user) => _selectedIds.contains(user.id));
      _selectedIds.clear();
    });
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('已删除选中用户')),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('用户管理'),
        actions: [
          if (_selectedIds.isNotEmpty)
            IconButton(
              icon: const Icon(Icons.delete),
              onPressed: _deleteSelected,
              tooltip: '删除选中',
            ),
        ],
      ),
      body: SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: DataTable(
          sortColumnIndex: _sortColumnIndex,
          sortAscending: _sortAscending,
          headingRowColor: WidgetStateProperty.all(Colors.grey[200]),
          columns: [
            DataColumn(
              label: const Text('ID'),
              onSort: _sortData,
            ),
            DataColumn(
              label: const Text('姓名'),
              onSort: _sortData,
            ),
            DataColumn(
              label: const Text('年龄'),
              numeric: true,
              onSort: _sortData,
            ),
            DataColumn(
              label: const Text('城市'),
              onSort: _sortData,
            ),
            const DataColumn(label: Text('状态')),
            const DataColumn(label: Text('操作')),
          ],
          rows: _users.map((user) => DataRow(
            selected: _selectedIds.contains(user.id),
            onSelectChanged: (selected) {
              setState(() {
                if (selected == true) {
                  _selectedIds.add(user.id);
                } else {
                  _selectedIds.remove(user.id);
                }
              });
            },
            cells: [
              DataCell(Text(user.id)),
              DataCell(Text(user.name)),
              DataCell(Text('${user.age}')),
              DataCell(Text(user.city)),
              DataCell(
                Container(
                  padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                  decoration: BoxDecoration(
                    color: user.status == '活跃' ? Colors.green : Colors.grey,
                    borderRadius: BorderRadius.circular(12),
                  ),
                  child: Text(
                    user.status,
                    style: const TextStyle(color: Colors.white, fontSize: 12),
                  ),
                ),
              ),
              DataCell(
                Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    IconButton(
                      icon: const Icon(Icons.edit, size: 20),
                      onPressed: () {
                        ScaffoldMessenger.of(context).showSnackBar(
                          SnackBar(content: Text('编辑: ${user.name}')),
                        );
                      },
                    ),
                    IconButton(
                      icon: const Icon(Icons.delete, size: 20, color: Colors.red),
                      onPressed: () {
                        setState(() {
                          _users.removeWhere((u) => u.id == user.id);
                        });
                      },
                    ),
                  ],
                ),
              ),
            ],
          )).toList(),
        ),
      ),
    );
  }
}

6.2 订单列表表格

dart 复制代码
class OrderTable extends StatelessWidget {
  const OrderTable({super.key});

  @override
  Widget build(BuildContext context) {
    final orders = [
      Order(id: 'ORD001', customer: '张三', amount: 299.00, status: '已完成', date: '2024-01-15'),
      Order(id: 'ORD002', customer: '李四', amount: 599.00, status: '处理中', date: '2024-01-14'),
      Order(id: 'ORD003', customer: '王五', amount: 199.00, status: '待付款', date: '2024-01-13'),
    ];

    return Scaffold(
      appBar: AppBar(title: const Text('订单列表')),
      body: SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: DataTable(
          columns: const [
            DataColumn(label: Text('订单号')),
            DataColumn(label: Text('客户')),
            DataColumn(label: Text('金额'), numeric: true),
            DataColumn(label: Text('状态')),
            DataColumn(label: Text('日期')),
          ],
          rows: orders.map((order) => DataRow(
            cells: [
              DataCell(Text(order.id)),
              DataCell(Text(order.customer)),
              DataCell(Text('¥${order.amount.toStringAsFixed(2)}')),
              DataCell(
                Container(
                  padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                  decoration: BoxDecoration(
                    color: _getStatusColor(order.status),
                    borderRadius: BorderRadius.circular(12),
                  ),
                  child: Text(
                    order.status,
                    style: const TextStyle(color: Colors.white, fontSize: 12),
                  ),
                ),
              ),
              DataCell(Text(order.date)),
            ],
          )).toList(),
        ),
      ),
    );
  }

  Color _getStatusColor(String status) {
    switch (status) {
      case '已完成':
        return Colors.green;
      case '处理中':
        return Colors.blue;
      case '待付款':
        return Colors.orange;
      default:
        return Colors.grey;
    }
  }
}

class Order {
  final String id;
  final String customer;
  final double amount;
  final String status;
  final String date;

  Order({
    required this.id,
    required this.customer,
    required this.amount,
    required this.status,
    required this.date,
  });
}

七、分页表格 PaginatedDataTable

当数据量较大时,可以使用 PaginatedDataTable 实现分页功能。

7.1 基本用法

dart 复制代码
class PaginatedDataTableExample extends StatefulWidget {
  const PaginatedDataTableExample({super.key});

  @override
  State<PaginatedDataTableExample> createState() => _PaginatedDataTableExampleState();
}

class _PaginatedDataTableExampleState extends State<PaginatedDataTableExample> {
  final List<User> _allUsers = List.generate(
    100,
    (index) => User(
      id: 'U${index.toString().padLeft(3, '0')}',
      name: '用户${index + 1}',
      age: 20 + (index % 30),
      city: ['北京', '上海', '广州', '深圳'][index % 4],
    ),
  );

  final Set<String> _selectedIds = {};

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('分页表格')),
      body: SingleChildScrollView(
        child: PaginatedDataTable(
          header: const Text('用户列表'),
          rowsPerPage: 10,
          availableRowsPerPage: const [5, 10, 20, 50],
          onRowsPerPageChanged: (value) {
            setState(() {});
          },
          columns: const [
            DataColumn(label: Text('ID')),
            DataColumn(label: Text('姓名')),
            DataColumn(label: Text('年龄'), numeric: true),
            DataColumn(label: Text('城市')),
          ],
          source: _UserDataSource(_allUsers, _selectedIds, (id, selected) {
            setState(() {
              if (selected) {
                _selectedIds.add(id);
              } else {
                _selectedIds.remove(id);
              }
            });
          }),
        ),
      ),
    );
  }
}

class _UserDataSource extends DataTableSource {
  final List<User> _users;
  final Set<String> _selectedIds;
  final void Function(String, bool) _onSelectChanged;

  _UserDataSource(this._users, this._selectedIds, this._onSelectChanged);

  @override
  DataRow? getRow(int index) {
    if (index >= _users.length) return null;
    final user = _users[index];
    return DataRow(
      selected: _selectedIds.contains(user.id),
      onSelectChanged: (selected) => _onSelectChanged(user.id, selected ?? false),
      cells: [
        DataCell(Text(user.id)),
        DataCell(Text(user.name)),
        DataCell(Text('${user.age}')),
        DataCell(Text(user.city)),
      ],
    );
  }

  @override
  bool get isRowCountApproximate => false;

  @override
  int get rowCount => _users.length;

  @override
  int get selectedRowCount => _selectedIds.length;
}

📊 PaginatedDataTable 属性速查表

属性 说明
header 表格标题
rowsPerPage 每页行数
availableRowsPerPage 可选的每页行数
onRowsPerPageChanged 每页行数变化回调
columns 列定义
source 数据源

八、最佳实践

8.1 性能优化

建议 说明
使用分页 大量数据使用 PaginatedDataTable
避免复杂单元格 简化 DataCell 的内容
懒加载数据 滚动时动态加载数据

8.2 样式设计

建议 说明
表头区分 使用不同的背景色区分表头
状态标签 使用彩色标签显示状态
合理间距 为表格添加适当的内边距

8.3 交互设计

建议 说明
批量操作 选中多行后提供批量操作按钮
确认删除 删除前弹出确认对话框
操作反馈 操作后显示 SnackBar 提示

九、总结

DataTable 是 Flutter 中用于创建数据表格的高级组件,提供了排序、选择、分页等功能。通过本文的学习,你应该已经掌握了:

  • DataTable 的基本用法和核心概念
  • DataColumn、DataRow、DataCell 的详细配置
  • 如何实现列排序和行选择功能
  • 如何使用 PaginatedDataTable 实现分页
  • 实际应用场景中的最佳实践

在实际开发中,DataTable 适合需要数据交互的后台管理系统。如果只需要简单的表格展示,建议使用 Table 组件。


九、完整示例代码

下面是一个完整的可运行示例,展示了 DataTable 的各种用法:

dart 复制代码
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'DataTable 示例',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const DataTableDemoPage(),
    );
  }
}

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

  @override
  State<DataTableDemoPage> createState() => _DataTableDemoPageState();
}

class _DataTableDemoPageState extends State<DataTableDemoPage> {
  int _selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('DataTable 示例'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      drawer: Drawer(
        child: ListView(
          children: [
            const DrawerHeader(
              decoration: BoxDecoration(color: Colors.blue),
              child: Text(
                'DataTable 示例',
                style: TextStyle(color: Colors.white, fontSize: 24),
              ),
            ),
            ListTile(
              leading: const Icon(Icons.table_chart),
              title: const Text('基础表格'),
              selected: _selectedIndex == 0,
              onTap: () {
                setState(() => _selectedIndex = 0);
                Navigator.pop(context);
              },
            ),
            ListTile(
              leading: const Icon(Icons.sort),
              title: const Text('排序功能'),
              selected: _selectedIndex == 1,
              onTap: () {
                setState(() => _selectedIndex = 1);
                Navigator.pop(context);
              },
            ),
            ListTile(
              leading: const Icon(Icons.check_box),
              title: const Text('选择功能'),
              selected: _selectedIndex == 2,
              onTap: () {
                setState(() => _selectedIndex = 2);
                Navigator.pop(context);
              },
            ),
            ListTile(
              leading: const Icon(Icons.pages),
              title: const Text('分页表格'),
              selected: _selectedIndex == 3,
              onTap: () {
                setState(() => _selectedIndex = 3);
                Navigator.pop(context);
              },
            ),
            ListTile(
              leading: const Icon(Icons.edit),
              title: const Text('可编辑表格'),
              selected: _selectedIndex == 4,
              onTap: () {
                setState(() => _selectedIndex = 4);
                Navigator.pop(context);
              },
            ),
          ],
        ),
      ),
      body: _buildPage(),
    );
  }

  Widget _buildPage() {
    switch (_selectedIndex) {
      case 0:
        return const BasicDataTablePage();
      case 1:
        return const SortableDataTablePage();
      case 2:
        return const SelectableDataTablePage();
      case 3:
        return const PaginatedDataTablePage();
      case 4:
        return const EditableDataTablePage();
      default:
        return const BasicDataTablePage();
    }
  }
}

class User {
  final String id;
  final String name;
  final int age;
  final String city;
  final String role;

  const User({
    required this.id,
    required this.name,
    required this.age,
    required this.city,
    required this.role,
  });
}

class BasicDataTablePage extends StatelessWidget {
  const BasicDataTablePage({super.key});

  final List<User> users = const [
    User(id: '001', name: '张三', age: 25, city: '北京', role: '工程师'),
    User(id: '002', name: '李四', age: 30, city: '上海', role: '设计师'),
    User(id: '003', name: '王五', age: 28, city: '广州', role: '产品经理'),
    User(id: '004', name: '赵六', age: 22, city: '深圳', role: '运营'),
    User(id: '005', name: '钱七', age: 35, city: '杭州', role: '架构师'),
  ];

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: DataTable(
        headingRowColor: WidgetStateProperty.all(Colors.blue[100]),
        dataRowColor: WidgetStateProperty.all(Colors.white),
        dividerThickness: 1,
        columns: const [
          DataColumn(label: Text('ID', style: TextStyle(fontWeight: FontWeight.bold))),
          DataColumn(label: Text('姓名', style: TextStyle(fontWeight: FontWeight.bold))),
          DataColumn(label: Text('年龄', style: TextStyle(fontWeight: FontWeight.bold))),
          DataColumn(label: Text('城市', style: TextStyle(fontWeight: FontWeight.bold))),
          DataColumn(label: Text('职位', style: TextStyle(fontWeight: FontWeight.bold))),
        ],
        rows: users.map((user) => DataRow(cells: [
          DataCell(Text(user.id)),
          DataCell(Text(user.name)),
          DataCell(Text('${user.age}')),
          DataCell(Text(user.city)),
          DataCell(Text(user.role)),
        ])).toList(),
      ),
    );
  }
}

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

  @override
  State<SortableDataTablePage> createState() => _SortableDataTablePageState();
}

class _SortableDataTablePageState extends State<SortableDataTablePage> {
  List<User> users = [
    User(id: '001', name: '张三', age: 25, city: '北京', role: '工程师'),
    User(id: '002', name: '李四', age: 30, city: '上海', role: '设计师'),
    User(id: '003', name: '王五', age: 28, city: '广州', role: '产品经理'),
    User(id: '004', name: '赵六', age: 22, city: '深圳', role: '运营'),
    User(id: '005', name: '钱七', age: 35, city: '杭州', role: '架构师'),
    User(id: '006', name: '孙八', age: 27, city: '成都', role: '测试工程师'),
    User(id: '007', name: '周九', age: 31, city: '武汉', role: '前端开发'),
  ];

  int? _sortColumnIndex;
  bool _sortAscending = true;

  void _sortData(int columnIndex, bool ascending) {
    setState(() {
      _sortColumnIndex = columnIndex;
      _sortAscending = ascending;
      users.sort((a, b) {
        int result;
        switch (columnIndex) {
          case 0:
            result = a.id.compareTo(b.id);
            break;
          case 1:
            result = a.name.compareTo(b.name);
            break;
          case 2:
            result = a.age.compareTo(b.age);
            break;
          case 3:
            result = a.city.compareTo(b.city);
            break;
          case 4:
            result = a.role.compareTo(b.role);
            break;
          default:
            result = 0;
        }
        return ascending ? result : -result;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: DataTable(
        sortColumnIndex: _sortColumnIndex,
        sortAscending: _sortAscending,
        headingRowColor: WidgetStateProperty.all(Colors.blue[100]),
        columns: [
          DataColumn(
            label: const Text('ID', style: TextStyle(fontWeight: FontWeight.bold)),
            onSort: _sortData,
          ),
          DataColumn(
            label: const Text('姓名', style: TextStyle(fontWeight: FontWeight.bold)),
            onSort: _sortData,
          ),
          DataColumn(
            label: const Text('年龄', style: TextStyle(fontWeight: FontWeight.bold)),
            numeric: true,
            onSort: _sortData,
          ),
          DataColumn(
            label: const Text('城市', style: TextStyle(fontWeight: FontWeight.bold)),
            onSort: _sortData,
          ),
          DataColumn(
            label: const Text('职位', style: TextStyle(fontWeight: FontWeight.bold)),
            onSort: _sortData,
          ),
        ],
        rows: users.map((user) => DataRow(cells: [
          DataCell(Text(user.id)),
          DataCell(Text(user.name)),
          DataCell(Text('${user.age}')),
          DataCell(Text(user.city)),
          DataCell(Text(user.role)),
        ])).toList(),
      ),
    );
  }
}

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

  @override
  State<SelectableDataTablePage> createState() => _SelectableDataTablePageState();
}

class _SelectableDataTablePageState extends State<SelectableDataTablePage> {
  final List<User> users = [
    User(id: '001', name: '张三', age: 25, city: '北京', role: '工程师'),
    User(id: '002', name: '李四', age: 30, city: '上海', role: '设计师'),
    User(id: '003', name: '王五', age: 28, city: '广州', role: '产品经理'),
    User(id: '004', name: '赵六', age: 22, city: '深圳', role: '运营'),
    User(id: '005', name: '钱七', age: 35, city: '杭州', role: '架构师'),
  ];

  final Set<String> _selectedIds = {};

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Container(
          padding: const EdgeInsets.all(16),
          color: Colors.grey[100],
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text('已选择 ${_selectedIds.length} 项'),
              if (_selectedIds.isNotEmpty)
                TextButton(
                  onPressed: () {
                    setState(() {
                      _selectedIds.clear();
                    });
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(content: Text('已取消所有选择')),
                    );
                  },
                  child: const Text('取消选择'),
                ),
            ],
          ),
        ),
        Expanded(
          child: SingleChildScrollView(
            padding: const EdgeInsets.all(16),
            child: DataTable(
              headingRowColor: WidgetStateProperty.all(Colors.blue[100]),
              showCheckboxColumn: true,
              columns: const [
                DataColumn(label: Text('ID', style: TextStyle(fontWeight: FontWeight.bold))),
                DataColumn(label: Text('姓名', style: TextStyle(fontWeight: FontWeight.bold))),
                DataColumn(label: Text('年龄', style: TextStyle(fontWeight: FontWeight.bold))),
                DataColumn(label: Text('城市', style: TextStyle(fontWeight: FontWeight.bold))),
                DataColumn(label: Text('职位', style: TextStyle(fontWeight: FontWeight.bold))),
              ],
              rows: users.map((user) => DataRow(
                selected: _selectedIds.contains(user.id),
                onSelectChanged: (selected) {
                  setState(() {
                    if (selected == true) {
                      _selectedIds.add(user.id);
                    } else {
                      _selectedIds.remove(user.id);
                    }
                  });
                },
                cells: [
                  DataCell(Text(user.id)),
                  DataCell(Text(user.name)),
                  DataCell(Text('${user.age}')),
                  DataCell(Text(user.city)),
                  DataCell(Text(user.role)),
                ],
              )).toList(),
            ),
          ),
        ),
      ],
    );
  }
}

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

  @override
  State<PaginatedDataTablePage> createState() => _PaginatedDataTablePageState();
}

class _PaginatedDataTablePageState extends State<PaginatedDataTablePage> {
  late List<User> _users;
  int _rowsPerPage = PaginatedDataTable.defaultRowsPerPage;

  @override
  void initState() {
    super.initState();
    _users = List.generate(
      50,
      (index) => User(
        id: '${index + 1}'.padLeft(3, '0'),
        name: '用户${index + 1}',
        age: 20 + (index % 20),
        city: ['北京', '上海', '广州', '深圳', '杭州'][index % 5],
        role: ['工程师', '设计师', '产品经理', '运营', '测试'][index % 5],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: PaginatedDataTable(
        header: const Text('用户列表'),
        rowsPerPage: _rowsPerPage,
        availableRowsPerPage: const [5, 10, 20, 50],
        onRowsPerPageChanged: (value) {
          setState(() {
            _rowsPerPage = value!;
          });
        },
        columns: const [
          DataColumn(label: Text('ID')),
          DataColumn(label: Text('姓名')),
          DataColumn(label: Text('年龄'), numeric: true),
          DataColumn(label: Text('城市')),
          DataColumn(label: Text('职位')),
        ],
        source: _UserDataSource(_users),
      ),
    );
  }
}

class _UserDataSource extends DataTableSource {
  final List<User> users;

  _UserDataSource(this.users);

  @override
  DataRow? getRow(int index) {
    if (index >= users.length) return null;
    final user = users[index];
    return DataRow(cells: [
      DataCell(Text(user.id)),
      DataCell(Text(user.name)),
      DataCell(Text('${user.age}')),
      DataCell(Text(user.city)),
      DataCell(Text(user.role)),
    ]);
  }

  @override
  bool get isRowCountApproximate => false;

  @override
  int get rowCount => users.length;

  @override
  int get selectedRowCount => 0;
}

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

  @override
  State<EditableDataTablePage> createState() => _EditableDataTablePageState();
}

class _EditableDataTablePageState extends State<EditableDataTablePage> {
  List<User> users = [
    User(id: '001', name: '张三', age: 25, city: '北京', role: '工程师'),
    User(id: '002', name: '李四', age: 30, city: '上海', role: '设计师'),
    User(id: '003', name: '王五', age: 28, city: '广州', role: '产品经理'),
    User(id: '004', name: '赵六', age: 22, city: '深圳', role: '运营'),
  ];

  void _editUser(int index) {
    final user = users[index];
    final nameController = TextEditingController(text: user.name);
    final ageController = TextEditingController(text: user.age.toString());
    final cityController = TextEditingController(text: user.city);
    final roleController = TextEditingController(text: user.role);

    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('编辑用户'),
        content: SingleChildScrollView(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              TextField(
                controller: nameController,
                decoration: const InputDecoration(labelText: '姓名'),
              ),
              TextField(
                controller: ageController,
                decoration: const InputDecoration(labelText: '年龄'),
                keyboardType: TextInputType.number,
              ),
              TextField(
                controller: cityController,
                decoration: const InputDecoration(labelText: '城市'),
              ),
              TextField(
                controller: roleController,
                decoration: const InputDecoration(labelText: '职位'),
              ),
            ],
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('取消'),
          ),
          ElevatedButton(
            onPressed: () {
              setState(() {
                users[index] = User(
                  id: user.id,
                  name: nameController.text,
                  age: int.tryParse(ageController.text) ?? user.age,
                  city: cityController.text,
                  role: roleController.text,
                );
              });
              Navigator.pop(context);
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(content: Text('已保存')),
              );
            },
            child: const Text('保存'),
          ),
        ],
      ),
    );
  }

  void _deleteUser(int index) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('确认删除'),
        content: Text('确定要删除 ${users[index].name} 吗?'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('取消'),
          ),
          ElevatedButton(
            onPressed: () {
              setState(() {
                users.removeAt(index);
              });
              Navigator.pop(context);
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(content: Text('已删除')),
              );
            },
            style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
            child: const Text('删除'),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: DataTable(
        headingRowColor: WidgetStateProperty.all(Colors.blue[100]),
        columns: const [
          DataColumn(label: Text('ID')),
          DataColumn(label: Text('姓名')),
          DataColumn(label: Text('年龄'), numeric: true),
          DataColumn(label: Text('城市')),
          DataColumn(label: Text('职位')),
          DataColumn(label: Text('操作')),
        ],
        rows: users.asMap().entries.map((entry) {
          final index = entry.key;
          final user = entry.value;
          return DataRow(cells: [
            DataCell(Text(user.id)),
            DataCell(Text(user.name)),
            DataCell(Text('${user.age}')),
            DataCell(Text(user.city)),
            DataCell(Text(user.role)),
            DataCell(Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                IconButton(
                  icon: const Icon(Icons.edit, color: Colors.blue),
                  onPressed: () => _editUser(index),
                ),
                IconButton(
                  icon: const Icon(Icons.delete, color: Colors.red),
                  onPressed: () => _deleteUser(index),
                ),
              ],
            )),
          ]);
        }).toList(),
      ),
    );
  }
}

参考资料

相关推荐
lili-felicity2 小时前
进阶实战 Flutter for OpenHarmony:SystemChrome 屏幕方向控制实战
flutter
lili-felicity2 小时前
进阶实战 Flutter for OpenHarmony:视频全屏播放系统 - 结合屏幕旋转
flutter·音视频
键盘鼓手苏苏3 小时前
Flutter for OpenHarmony:injector 轻量级依赖注入库(比 GetIt 更简单的选择) 深度解析与鸿蒙适配指南
css·网络·flutter·华为·rust·harmonyos
lili-felicity3 小时前
进阶实战 Flutter for OpenHarmony:video_player 第三方库实战 - 专业级视频播放
flutter
不爱吃糖的程序媛3 小时前
Flutter性能监控插件鸿蒙适配实战指南
flutter·harmonyos
lili-felicity4 小时前
进阶实战 Flutter for OpenHarmony:image_picker 第三方库实战 - 图片选择
flutter
键盘鼓手苏苏6 小时前
Flutter for OpenHarmony:markdown 纯 Dart 解析引擎(将文本转化为结构化 HTML/UI) 深度解析与鸿蒙适配指南
前端·网络·算法·flutter·ui·html·harmonyos
恋猫de小郭12 小时前
丰田正在使用 Flutter 开发游戏引擎 Fluorite
android·前端·flutter