Flutter学习记录——12.表格组件

今天药忘吃喽~ 2023-06-19 02:58 107阅读 0赞

文章目录

  • 1.Table Widget
  • 2.DataTable Widget
  • 3.PaginatedDataTable Widget
  • 4.总结

1.Table Widget

我们先看下表格绘制的第一种实现组件:Table。Table 的继承关系:

Table -> RenderObjectWidget -> Widget

Table 中的每一行用 TableRow 组件,列数用 columnWidths 属性控制。

我们看下 Table 的构造方法:

  1. Table({
  2. Key key,
  3. // 每行的TableRow集合
  4. this.children = const <TableRow>[],
  5. // 设置每列的宽度
  6. this.columnWidths,
  7. // 默认每列宽度值,默认情况下均分
  8. this.defaultColumnWidth = const FlexColumnWidth(1.0),
  9. // 文字方向
  10. this.textDirection,
  11. // 表格边框设置
  12. this.border,
  13. // 默认垂直方向的对齐方式
  14. this.defaultVerticalAlignment = TableCellVerticalAlignment.top,
  15. // defaultVerticalAlignment为baseline的时候,配置生效
  16. this.textBaseline,
  17. })

通过实例来学习 Table 的用法:

  1. class TableSamplesState extends State<TableSamples> {
  2. @override
  3. void initState() {
  4. super.initState();
  5. }
  6. @override
  7. Widget build(BuildContext context) {
  8. return Scaffold(
  9. appBar: AppBar(title: Text('Table Demo'), primary: true),
  10. body: Padding(
  11. padding: EdgeInsets.all(10),
  12. // 使用Table绘制表格
  13. child: Table(
  14. // 有很多种设置宽度方式
  15. columnWidths: {
  16. ///固定列宽度
  17. 0: FixedColumnWidth(50),
  18. ///弹性列宽度
  19. 1: FlexColumnWidth(1),
  20. ///宽度占所在容器的百分比(0-1)
  21. 2: FractionColumnWidth(0.5),
  22. 3: IntrinsicColumnWidth(flex: 0.2),
  23. 4: MaxColumnWidth(
  24. FixedColumnWidth(100.0), FractionColumnWidth(0.1)),
  25. ///大于容器10%宽度,但小于等于100px
  26. 5: MinColumnWidth(
  27. FixedColumnWidth(100.0), FractionColumnWidth(0.1)),
  28. },
  29. // 设置表格边框
  30. border: TableBorder.all(color: Colors.black, width: 1),
  31. children: <TableRow>[
  32. // 每行内容设置
  33. TableRow(children: <Widget>[
  34. // 每个表格单元
  35. TableCell(
  36. verticalAlignment: TableCellVerticalAlignment.middle,
  37. child: Center(
  38. child: Text(
  39. 'Title1',
  40. style:
  41. TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
  42. ),
  43. ),
  44. ),
  45. TableCell(
  46. verticalAlignment: TableCellVerticalAlignment.middle,
  47. child: Center(
  48. child: Text(
  49. 'Title2',
  50. style:
  51. TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
  52. ),
  53. ),
  54. ),
  55. TableCell(
  56. verticalAlignment: TableCellVerticalAlignment.middle,
  57. child: Center(
  58. child: Text(
  59. 'Title3',
  60. style:
  61. TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
  62. ),
  63. ),
  64. ),
  65. ]),
  66. TableRow(children: <Widget>[
  67. TableCell(
  68. child: Text('data1'),
  69. ),
  70. TableCell(
  71. child: Text('data2'),
  72. ),
  73. TableCell(
  74. child: Text('data3'),
  75. ),
  76. ]),
  77. TableRow(children: <Widget>[
  78. TableCell(
  79. child: Text('data1'),
  80. ),
  81. TableCell(
  82. child: Text('data2'),
  83. ),
  84. TableCell(
  85. child: Text('data3'),
  86. ),
  87. ]),
  88. TableRow(children: <Widget>[
  89. TableCell(
  90. child: Text('data1'),
  91. ),
  92. TableCell(
  93. child: Text('data2'),
  94. ),
  95. TableCell(
  96. child: Text('data3'),
  97. ),
  98. ]),
  99. TableRow(children: <Widget>[
  100. TableCell(
  101. child: Text('data1'),
  102. ),
  103. TableCell(
  104. child: Text('data2'),
  105. ),
  106. TableCell(
  107. child: Text('data3'),
  108. ),
  109. ]),
  110. ],
  111. ),
  112. ));
  113. }
  114. }

运行效果如图:

Table

可见,Table 的用法很简单。

2.DataTable Widget

下面我们学习一下绘制表格的另一个组件:DataTable,这个功能更加强大,比较常用。

DataTable 继承自 StatelessWidget,是一个无状态组件。

我们看下 DataTable 的构造方法:

  1. DataTable({
  2. Key key,
  3. // 设置表头
  4. @required this.columns,
  5. // 列排序索引
  6. this.sortColumnIndex,
  7. // 是否升序排序,默认为升序
  8. this.sortAscending = true,
  9. // 点击全选
  10. this.onSelectAll,
  11. // 表格每行内容
  12. @required this.rows,
  13. })

在 DataTable 中 columns 属性里放置的是 DataColumn 来设置列属性,我们看下 DataColumn 的构造方法:

  1. const DataColumn({
  2. // 标签,可使用文本或者尺寸为18的图标
  3. @required this.label,
  4. // 工具提示
  5. this.tooltip,
  6. // 是否包含数字
  7. this.numeric = false,
  8. // 排序时调用
  9. this.onSort,
  10. })

在 DataTable 中 rows 属性里放置的是 DataRow 来设置行内容和属性,我们看下 DataRow 的构造方法:

  1. const DataRow({
  2. this.key,
  3. // 是否选中
  4. this.selected = false,
  5. // 选中状态改变时回调
  6. this.onSelectChanged,
  7. // 子表格单元
  8. @required this.cells,
  9. })

这里的每个子表格单元使用的是 DataCell 来设置内容和属性,看看 DataCell 的构造方法:

  1. const DataCell(
  2. // 子控件,一般为Text或DropdownButton
  3. this.child, {
  4. // 是否为占位符,若子控件为Text,显示占位符文本样式
  5. this.placeholder = false,
  6. // 是否显示编辑图标,配合onTab使用
  7. this.showEditIcon = false,
  8. // 点击回调
  9. this.onTap,
  10. })

接下来我们通过一个实例来看下 DataTable 的用法:

  1. class DataTableState extends State<DataTableSamples> {
  2. @override
  3. void initState() {
  4. super.initState();
  5. }
  6. @override
  7. Widget build(BuildContext context) {
  8. return Scaffold(
  9. appBar: AppBar(title: Text('DataTable Demo'), primary: true),
  10. body: DataTable(
  11. // 行
  12. rows: <DataRow>[
  13. // 每行内容设置
  14. DataRow(
  15. cells: <DataCell>[
  16. // 子表格单元
  17. DataCell(Text('data1'), onTap: onTap),
  18. DataCell(Text('data2'), onTap: onTap),
  19. DataCell(Text('data3'), onTap: onTap),
  20. ],
  21. ),
  22. DataRow(
  23. cells: <DataCell>[
  24. DataCell(Text('data1'), onTap: onTap),
  25. DataCell(Text('data2'), onTap: onTap),
  26. DataCell(Text('data3'), onTap: onTap),
  27. ],
  28. ),
  29. DataRow(
  30. cells: <DataCell>[
  31. DataCell(Text('data1'), onTap: onTap),
  32. DataCell(Text('data2'), onTap: onTap),
  33. DataCell(Text('data3'), onTap: onTap),
  34. ],
  35. ),
  36. DataRow(
  37. cells: <DataCell>[
  38. DataCell(Text('data1'), onTap: onTap),
  39. DataCell(Text('data2'), onTap: onTap),
  40. DataCell(Text('data3'), onTap: onTap),
  41. ],
  42. ),
  43. DataRow(
  44. cells: <DataCell>[
  45. DataCell(Text('data1'), onTap: onTap),
  46. DataCell(Text('data2'), onTap: onTap),
  47. DataCell(Text('data3'), onTap: onTap),
  48. ],
  49. ),
  50. ],
  51. // 列
  52. columns: <DataColumn>[
  53. DataColumn(label: Text('DataColumn1')),
  54. DataColumn(label: Text('DataColumn2')),
  55. DataColumn(label: Text('DataColumn3')),
  56. ],
  57. ));
  58. }
  59. }
  60. void onTap() {
  61. print('data onTap');
  62. }

运行效果如图:

avatar

3.PaginatedDataTable Widget

这里拓展一下,PaginatedDataTable 也是一个 DataTable,主要用来绘制有分页类型的表格。

PaginatedDataTable 继承自 StatefulWidget,是一个有状态组件。

PaginatedDataTable 的构造方法如下:

  1. PaginatedDataTable({
  2. Key key,
  3. // 表名,通常为Text,也可以是ButtonBar/FlatButton
  4. @required this.header,
  5. // 动作,List<Widget>集合,内部子控件大小宽高要24.0,padding为8.0
  6. this.actions,
  7. // 列集合
  8. @required this.columns,
  9. // 列排序索引
  10. this.sortColumnIndex,
  11. // 是否升序排序
  12. this.sortAscending = true,
  13. // 点击全选回调
  14. this.onSelectAll,
  15. // 初始索引
  16. this.initialFirstRowIndex = 0,
  17. // 页数更改监听,左右箭头点击时
  18. this.onPageChanged,
  19. // 默认一页显示的行数,默认为10
  20. this.rowsPerPage = defaultRowsPerPage,
  21. // 可选择页数
  22. this.availableRowsPerPage = const <int>[defaultRowsPerPage, defaultRowsPerPage * 2, defaultRowsPerPage * 5, defaultRowsPerPage * 10],
  23. // 点击可选择页数下拉监听
  24. this.onRowsPerPageChanged,
  25. this.dragStartBehavior = DragStartBehavior.down,
  26. // 表格数据源DataTableSource
  27. @required this.source
  28. })

PaginatedDataTable 在使用时,外层要是一个 ListView 或 ScrollView 这种可滚动容器才可以使用。

在使用 PaginatedDataTable 时,最重要的一个不同就是要设置 DataTableSource,我们需要编写提供一个 DataTableSource 表格数据源提供给表格数据。

PaginatedDataTable 的用法实例:

  1. class PaginatedDataTableState extends State<PaginatedDataTableSamples> {
  2. TableDataSource _dataSource = TableDataSource();
  3. int _defalutRowPageCount = 8;
  4. int _sortColumnIndex;
  5. bool _sortAscending = true;
  6. @override
  7. void initState() {
  8. super.initState();
  9. }
  10. @override
  11. Widget build(BuildContext context) {
  12. return Scaffold(
  13. appBar: AppBar(title: Text('PaginatedDataTable Demo'), primary: true),
  14. // 外层用ListView包裹
  15. body: ListView(
  16. padding: EdgeInsets.all(10),
  17. children: <Widget>[
  18. // PaginatedDataTable
  19. PaginatedDataTable(
  20. // 表格数据源
  21. source: _dataSource,
  22. // 默认为0
  23. initialFirstRowIndex: 0,
  24. // 全选操作
  25. onSelectAll: (bool checked) {
  26. _dataSource.selectAll(checked);
  27. },
  28. // 每页显示的行数
  29. rowsPerPage: _defalutRowPageCount,
  30. // 每页显示数量改变后的回调
  31. onRowsPerPageChanged: (value) {
  32. setState(() {
  33. _defalutRowPageCount = value;
  34. });
  35. },
  36. // 设置每页可以显示的行数值列表选项
  37. availableRowsPerPage: [5, 8],
  38. // 翻页操作回调
  39. onPageChanged: (value) {
  40. print('$value');
  41. },
  42. // 是否升序排序
  43. sortAscending: _sortAscending,
  44. sortColumnIndex: _sortColumnIndex,
  45. // 表格头部
  46. header: Text('Data Header'),
  47. // 列
  48. columns: <DataColumn>[
  49. DataColumn(label: Text('名字')),
  50. DataColumn(
  51. label: Text('价格'),
  52. // 加入排序操作
  53. onSort: (int columnIndex, bool ascending) {
  54. _sort<num>((Shop p) => p.price, columnIndex, ascending);
  55. }),
  56. DataColumn(label: Text('类型')),
  57. ],
  58. ),
  59. ],
  60. ));
  61. }
  62. //排序关联_sortColumnIndex,_sortAscending
  63. void _sort<T>(Comparable<T> getField(Shop s), int index, bool b) {
  64. _dataSource._sort(getField, b);
  65. setState(() {
  66. this._sortColumnIndex = index;
  67. this._sortAscending = b;
  68. });
  69. }
  70. }
  71. class Shop {
  72. final String name;
  73. final int price;
  74. final String type;
  75. // 默认为未选中
  76. bool selected = false;
  77. Shop(this.name, this.price, this.type);
  78. }
  79. class TableDataSource extends DataTableSource {
  80. final List<Shop> shops = <Shop>[
  81. Shop('name', 100, '家电'),
  82. Shop('name2', 130, '手机'),
  83. Shop('三星', 130, '手机'),
  84. Shop('三星', 130, '手机'),
  85. Shop('三星', 130, '手机'),
  86. Shop('海信', 100, '家电'),
  87. Shop('TCL', 100, '家电'),
  88. ];
  89. int _selectedCount = 0;
  90. ///根据位置获取内容行
  91. @override
  92. DataRow getRow(int index) {
  93. Shop shop = shops.elementAt(index);
  94. return DataRow.byIndex(
  95. cells: <DataCell>[
  96. DataCell(
  97. Text('${ shop.name}'),
  98. placeholder: true,
  99. ),
  100. DataCell(Text('${ shop.price}'), showEditIcon: true),
  101. DataCell(Text('${ shop.type}'), showEditIcon: false),
  102. ],
  103. selected: shop.selected,
  104. index: index,
  105. onSelectChanged: (bool isSelected) {
  106. if (shop.selected != isSelected) {
  107. _selectedCount += isSelected ? 1 : -1;
  108. shop.selected = isSelected;
  109. notifyListeners();
  110. }
  111. });
  112. }
  113. @override
  114. ///行数是否不确定
  115. bool get isRowCountApproximate => false;
  116. @override
  117. ///行数
  118. int get rowCount => shops.length;
  119. @override
  120. ///选中的行数
  121. int get selectedRowCount => _selectedCount;
  122. void selectAll(bool checked) {
  123. for (Shop shop in shops) {
  124. shop.selected = checked;
  125. }
  126. _selectedCount = checked ? shops.length : 0;
  127. notifyListeners();
  128. }
  129. //排序,
  130. void _sort<T>(Comparable<T> getField(Shop shop), bool b) {
  131. shops.sort((Shop s1, Shop s2) {
  132. if (!b) {
  133. //两个项进行交换
  134. final Shop temp = s1;
  135. s1 = s2;
  136. s2 = temp;
  137. }
  138. final Comparable<T> s1Value = getField(s1);
  139. final Comparable<T> s2Value = getField(s2);
  140. return Comparable.compare(s1Value, s2Value);
  141. });
  142. notifyListeners();
  143. }
  144. }
  145. void onTap() {
  146. print('data onTap');
  147. }

运行效果如图:

avatar

4.总结

本节博客主要是给大家讲解了 Flutter 的表格绘制组件的用法和特点。

  • 重点掌握 DataTable 和 PaginatedDataTable 的用法。
  • 实践一下这几个 Widget 使用方法,尝试写一个课程表表格页面。

发表评论

表情:
评论列表 (有 0 条评论,107人围观)

还没有评论,来说两句吧...

相关阅读