Flutter PaginatedDataTable 的一些样式设置 及 上移/下移行
PaginatedDataTable 组件
Flutter 的 PaginatedDataTable 组件是在 DataTable 组件上又包装了一些分页的功能。
该示例实现了下面几个点:
- 表头背景色
- 表头文字样式
- 行分隔线颜色
- 鼠标悬停时数据行颜色(现在还有问题,当设置了数据行颜色时,悬停时颜色不起作用)
- 数据行选中时的颜色
- 选中数据行的上移/下移
- 分页箭头颜色
Don't talk! Show me the code!
数据行的类
data_item.dart
class DataItem {
final String text;
final bool selected;
const DataItem({this.text = '', this.selected = false});
}
数据源
data_source.dart
import 'package:flutter/material.dart';
import 'data_item.dart';
class DataSource extends DataTableSource {
final List<DataItem> list;
final Function(int index, bool isSeleted) selectChanged;
DataSource({required this.list, required this.selectChanged});
@override
DataRow getRow(int index) {
return DataRow.byIndex(
index: index,
selected: list[index].selected,
onSelectChanged: (isSelected) {
// 处理
selectChanged(index, isSelected ?? false);
},
cells: [
DataCell(
SizedBox(
child: Text(index.toString()),
),
),
DataCell(
SizedBox(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 24.0,
alignment: Alignment.centerLeft,
child: Text('hello_${list[index].text}'),
),
Container(
height: 20.0,
alignment: Alignment.centerLeft,
child: Text('world_${list[index].text}'),
),
],
),
),
),
DataCell(
SizedBox(
child: OutlinedButton(
onPressed: () {
// 处理
debugPrint('Hello, world');
},
child: const Text('go'),
),
),
),
],
);
}
@override
bool get isRowCountApproximate => false;
@override
int get rowCount => list.length;
@override
int get selectedRowCount => 0;
}
主程序
main.dart
import 'package:flutter/material.dart';
import 'package:untitled/data_item.dart';
import 'data_source.dart';
void main() {
runApp(const App());
}
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<HomePage> {
// 分页DataTable的 Key,用于改变 分页DataTable的状态
final keyDataTable = GlobalKey<PaginatedDataTableState>();
// 数据
late List<DataItem> list = [];
// 选中行
int selectedIndex = -1;
final int totalRowCount = 21;
@override
void initState() {
super.initState();
list = dataItemList();
}
// 原始数据
List<DataItem> dataItemList() {
final List<DataItem> items = [];
for (int i = 0; i < totalRowCount; i++) {
items.add(DataItem(text: i.toString()));
}
return items;
}
// 选中/反选 某一行
void selectChanged(int index, bool isSeleted) {
final List<DataItem> items = [];
for (int i = 0; i < totalRowCount; i++) {
bool selected = false;
if (i == index && isSeleted) {
selected = true;
}
items.add(DataItem(text: list[i].text, selected: selected));
}
selectedIndex = index;
setState(() {
list = items;
});
}
// 上移选中行
void up(int index) {
// 当前选中行
DataItem selectedItem = list[index];
// 上一行
DataItem? item;
if (index - 1 >= 0) {
item = list[index - 1];
}
final List<DataItem> items = [];
for (int i = 0; i < totalRowCount; i++) {
if (i == index - 1) {
// 当前选中行放到上一行
items.add(selectedItem);
} else if (i == index && item != null) {
// 上一行放到当前选中行
items.add(item);
} else {
// 其它行直接添加
items.add(list[i]);
}
}
if (index - 1 >= 0) {
// 修改选中行
selectedIndex = index - 1;
// 跳转到选中行(主要用于翻页时自动跳转到对应页)
keyDataTable.currentState!.pageTo(selectedIndex);
}
// 改变绑定的数据
setState(() {
list = items;
});
}
// 下移选中行
void down(int index) {
// 当前选中行
DataItem selectedItem = list[index];
// 下一行
DataItem? item;
if (index + 1 < list.length) {
item = list[index + 1];
}
final List<DataItem> items = [];
for (int i = 0; i < 21; i++) {
if (i == index + 1) {
// 当前选中行放到下一行
items.add(selectedItem);
} else if (i == index && item != null) {
// 下一行放到当前选中行
items.add(item);
} else {
// 其它行直接添加
items.add(list[i]);
}
}
if (index + 1 < list.length) {
// 修改选中行
selectedIndex = index + 1;
// 跳转到选中行(主要用于翻页时自动跳转到对应页)
keyDataTable.currentState!.pageTo(selectedIndex);
}
// 改变绑定的数据
setState(() {
list = items;
});
}
@override
Widget build(BuildContext context) {
final double width = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: const Text('可分页的 DataTable'),
),
body: SingleChildScrollView(
child: Column(
children: [
Row(
children: [
// 下移选中行
Padding(
padding: const EdgeInsets.all(4.0),
child: OutlinedButton(
child: const Icon(Icons.arrow_downward),
onPressed: () {
down(selectedIndex);
},
),
),
// 上移选中行
Padding(
padding: const EdgeInsets.all(4.0),
child: OutlinedButton(
child: const Icon(Icons.arrow_upward),
onPressed: () {
up(selectedIndex);
},
),
),
],
),
Theme(
data: Theme.of(context).copyWith(
// DataTable 行分隔线的颜色
dividerColor: Colors.red,
),
child: DataTableTheme(
data: DataTableTheme.of(context).copyWith(
// 表头背景色
headingRowColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
return Theme.of(context).primaryColorDark; //
}),
// 表头文字样式(颜色等)
headingTextStyle: const TextStyle(color: Colors.orangeAccent),
dataRowColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
// 点击、获得焦点、选中时的状态
const Set<MaterialState> stateA = <MaterialState>{
MaterialState.pressed,
MaterialState.focused,
MaterialState.selected,
};
// 点击、获得焦点、选中时的颜色
if (states.any(stateA.contains)) {
return Colors.lightBlue;
}
// 鼠标经过时的状态
const Set<MaterialState> stateB = <MaterialState>{
MaterialState.hovered,
};
// 鼠标经过时的颜色
if (states.any(stateB.contains)) {
return Colors.cyan;
}
// 数据行的颜色,这里如果设置颜色的话,上面的鼠标经过则不起作用。
return Colors.transparent;
}),
),
child: PaginatedDataTable(
// 用于代码控制分页DataTable的状态
key: keyDataTable,
horizontalMargin: 8,
columnSpacing: 8,
dataRowHeight: 48,
showCheckboxColumn: false,
// 分页箭头颜色(第一页的向左箭头和最后一页的向右箭头无法设置灰度)
arrowHeadColor: Colors.green,
rowsPerPage: 10,
source: DataSource(list: list, selectChanged: selectChanged),
columns: [
DataColumn(
label: Container(
constraints: BoxConstraints(minWidth: width * 0.1),
child: const Text('行号'),
),
),
DataColumn(
label: Container(
// alignment: Alignment.center,
constraints: BoxConstraints(minWidth: width * 0.75),
child: const Text('内容'),
),
),
DataColumn(
label: Container(
alignment: Alignment.center,
constraints: BoxConstraints(minWidth: width * 0.05),
child: const Text('处理'),
),
),
],
),
),
),
],
),
),
);
}
}