基础入门 Flutter for OpenHarmony:DataTable 数据表格组件详解
在移动应用开发中,数据表格是一种常见的数据管理方式。Flutter 提供了 DataTable 组件,专门用于展示和管理结构化数据。与 Table 组件不同,DataTable 提供了排序、选择、分页等高级功能,适合需要数据交互的场景。DataColumn 用于定义表格的列,包括标题、排序、对齐等属性。DataRow 用于定义表格的一行数据,支持选择、状态等功能。DataTable 是 Flutt

欢迎加入开源鸿蒙跨平台社区: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 需要设置 columns 和 rows 参数:
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 行选择功能
通过 selected 和 onSelectChanged 实现行选择:
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 可编辑单元格
通过 showEditIcon 和 onTap 实现可编辑效果:
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(),
),
);
}
}
参考资料
更多推荐



所有评论(0)