在这里插入图片描述

欢迎加入开源鸿蒙跨平台社区: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 参数:

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 的属性设置列宽和对齐方式:

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 的基础用法:

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

  
  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 基本用法

DataColumn(
  label: Text('姓名'),
  numeric: false,
  tooltip: '用户的姓名',
)

3.2 排序功能

通过 onSort 回调实现列排序:

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

  
  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;
      });
    });
  }

  
  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 基本用法

DataRow(
  cells: [
    DataCell(Text('张三')),
    DataCell(Text('25')),
    DataCell(Text('北京')),
  ],
)

4.2 行选择功能

通过 selectedonSelectChanged 实现行选择:

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

  
  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 = {};

  
  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 支持不同的状态:

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 基本用法

DataCell(Text('张三'))

5.2 可编辑单元格

通过 showEditIcononTap 实现可编辑效果:

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

5.3 占位符单元格

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

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 用户管理表格

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

  
  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('已删除选中用户')),
    );
  }

  
  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 订单列表表格

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

  
  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 基本用法

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

  
  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 = {};

  
  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);

  
  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)),
      ],
    );
  }

  
  bool get isRowCountApproximate => false;

  
  int get rowCount => _users.length;

  
  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 的各种用法:

import 'package:flutter/material.dart';

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

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

  
  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});

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

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

  
  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: '架构师'),
  ];

  
  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});

  
  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;
      });
    });
  }

  
  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});

  
  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 = {};

  
  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});

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

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

  
  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],
      ),
    );
  }

  
  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);

  
  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)),
    ]);
  }

  
  bool get isRowCountApproximate => false;

  
  int get rowCount => users.length;

  
  int get selectedRowCount => 0;
}

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

  
  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('删除'),
          ),
        ],
      ),
    );
  }

  
  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(),
      ),
    );
  }
}

参考资料

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐