文章目錄
- 1.Table Widget
- 2.DataTable Widget
- 3.PaginatedDataTable Widget
- 4.總結
1.Table Widget
我們先看下表格繪制的第一種實作元件:Table。Table 的繼承關系:
Table -> RenderObjectWidget -> Widget
Table 中的每一行用 TableRow 元件,列數用 columnWidths 屬性控制。
我們看下 Table 的構造方法:
Table({
Key key,
// 每行的TableRow集合
this.children = const <TableRow>[],
// 設定每列的寬度
this.columnWidths,
// 預設每列寬度值,預設情況下均分
this.defaultColumnWidth = const FlexColumnWidth(1.0),
// 文字方向
this.textDirection,
// 表格邊框設定
this.border,
// 預設垂直方向的對齊方式
this.defaultVerticalAlignment = TableCellVerticalAlignment.top,
// defaultVerticalAlignment為baseline的時候,配置生效
this.textBaseline,
})
通過執行個體來學習 Table 的用法:
class TableSamplesState extends State<TableSamples> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Table Demo'), primary: true),
body: Padding(
padding: EdgeInsets.all(10),
// 使用Table繪制表格
child: Table(
// 有很多種設定寬度方式
columnWidths: {
///固定列寬度
0: FixedColumnWidth(50),
///彈性列寬度
1: FlexColumnWidth(1),
///寬度占所在容器的百分比(0-1)
2: FractionColumnWidth(0.5),
3: IntrinsicColumnWidth(flex: 0.2),
4: MaxColumnWidth(
FixedColumnWidth(100.0), FractionColumnWidth(0.1)),
///大于容器10%寬度,但小于等于100px
5: MinColumnWidth(
FixedColumnWidth(100.0), FractionColumnWidth(0.1)),
},
// 設定表格邊框
border: TableBorder.all(color: Colors.black, width: 1),
children: <TableRow>[
// 每行内容設定
TableRow(children: <Widget>[
// 每個表格單元
TableCell(
verticalAlignment: TableCellVerticalAlignment.middle,
child: Center(
child: Text(
'Title1',
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
),
TableCell(
verticalAlignment: TableCellVerticalAlignment.middle,
child: Center(
child: Text(
'Title2',
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
),
TableCell(
verticalAlignment: TableCellVerticalAlignment.middle,
child: Center(
child: Text(
'Title3',
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
),
]),
TableRow(children: <Widget>[
TableCell(
child: Text('data1'),
),
TableCell(
child: Text('data2'),
),
TableCell(
child: Text('data3'),
),
]),
TableRow(children: <Widget>[
TableCell(
child: Text('data1'),
),
TableCell(
child: Text('data2'),
),
TableCell(
child: Text('data3'),
),
]),
TableRow(children: <Widget>[
TableCell(
child: Text('data1'),
),
TableCell(
child: Text('data2'),
),
TableCell(
child: Text('data3'),
),
]),
TableRow(children: <Widget>[
TableCell(
child: Text('data1'),
),
TableCell(
child: Text('data2'),
),
TableCell(
child: Text('data3'),
),
]),
],
),
));
}
}
運作效果如圖:
Table
可見,Table 的用法很簡單。
2.DataTable Widget
下面我們學習一下繪制表格的另一個元件:DataTable,這個功能更加強大,比較常用。
DataTable 繼承自 StatelessWidget,是一個無狀态元件。
我們看下 DataTable 的構造方法:
DataTable({
Key key,
// 設定表頭
@required this.columns,
// 列排序索引
this.sortColumnIndex,
// 是否升序排序,預設為升序
this.sortAscending = true,
// 點選全選
this.onSelectAll,
// 表格每行内容
@required this.rows,
})
在 DataTable 中 columns 屬性裡放置的是 DataColumn 來設定列屬性,我們看下 DataColumn 的構造方法:
const DataColumn({
// 标簽,可使用文本或者尺寸為18的圖示
@required this.label,
// 工具提示
this.tooltip,
// 是否包含數字
this.numeric = false,
// 排序時調用
this.onSort,
})
在 DataTable 中 rows 屬性裡放置的是 DataRow 來設定行内容和屬性,我們看下 DataRow 的構造方法:
const DataRow({
this.key,
// 是否選中
this.selected = false,
// 選中狀态改變時回調
this.onSelectChanged,
// 子表格單元
@required this.cells,
})
這裡的每個子表格單元使用的是 DataCell 來設定内容和屬性,看看 DataCell 的構造方法:
const DataCell(
// 子控件,一般為Text或DropdownButton
this.child, {
// 是否為占位符,若子控件為Text,顯示占位符文本樣式
this.placeholder = false,
// 是否顯示編輯圖示,配合onTab使用
this.showEditIcon = false,
// 點選回調
this.onTap,
})
接下來我們通過一個執行個體來看下 DataTable 的用法:
class DataTableState extends State<DataTableSamples> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('DataTable Demo'), primary: true),
body: DataTable(
// 行
rows: <DataRow>[
// 每行内容設定
DataRow(
cells: <DataCell>[
// 子表格單元
DataCell(Text('data1'), onTap: onTap),
DataCell(Text('data2'), onTap: onTap),
DataCell(Text('data3'), onTap: onTap),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('data1'), onTap: onTap),
DataCell(Text('data2'), onTap: onTap),
DataCell(Text('data3'), onTap: onTap),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('data1'), onTap: onTap),
DataCell(Text('data2'), onTap: onTap),
DataCell(Text('data3'), onTap: onTap),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('data1'), onTap: onTap),
DataCell(Text('data2'), onTap: onTap),
DataCell(Text('data3'), onTap: onTap),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('data1'), onTap: onTap),
DataCell(Text('data2'), onTap: onTap),
DataCell(Text('data3'), onTap: onTap),
],
),
],
// 列
columns: <DataColumn>[
DataColumn(label: Text('DataColumn1')),
DataColumn(label: Text('DataColumn2')),
DataColumn(label: Text('DataColumn3')),
],
));
}
}
void onTap() {
print('data onTap');
}
運作效果如圖:
3.PaginatedDataTable Widget
這裡拓展一下,PaginatedDataTable 也是一個 DataTable,主要用來繪制有分頁類型的表格。
PaginatedDataTable 繼承自 StatefulWidget,是一個有狀态元件。
PaginatedDataTable 的構造方法如下:
PaginatedDataTable({
Key key,
// 表名,通常為Text,也可以是ButtonBar/FlatButton
@required this.header,
// 動作,List<Widget>集合,内部子控件大小寬高要24.0,padding為8.0
this.actions,
// 列集合
@required this.columns,
// 列排序索引
this.sortColumnIndex,
// 是否升序排序
this.sortAscending = true,
// 點選全選回調
this.onSelectAll,
// 初始索引
this.initialFirstRowIndex = 0,
// 頁數更改監聽,左右箭頭點選時
this.onPageChanged,
// 預設一頁顯示的行數,預設為10
this.rowsPerPage = defaultRowsPerPage,
// 可選擇頁數
this.availableRowsPerPage = const <int>[defaultRowsPerPage, defaultRowsPerPage * 2, defaultRowsPerPage * 5, defaultRowsPerPage * 10],
// 點選可選擇頁數下拉監聽
this.onRowsPerPageChanged,
this.dragStartBehavior = DragStartBehavior.down,
// 表格資料源DataTableSource
@required this.source
})
PaginatedDataTable 在使用時,外層要是一個 ListView 或 ScrollView 這種可滾動容器才可以使用。
在使用 PaginatedDataTable 時,最重要的一個不同就是要設定 DataTableSource,我們需要編寫提供一個 DataTableSource 表格資料源提供給表格資料。
PaginatedDataTable 的用法執行個體:
class PaginatedDataTableState extends State<PaginatedDataTableSamples> {
TableDataSource _dataSource = TableDataSource();
int _defalutRowPageCount = 8;
int _sortColumnIndex;
bool _sortAscending = true;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('PaginatedDataTable Demo'), primary: true),
// 外層用ListView包裹
body: ListView(
padding: EdgeInsets.all(10),
children: <Widget>[
// PaginatedDataTable
PaginatedDataTable(
// 表格資料源
source: _dataSource,
// 預設為0
initialFirstRowIndex: 0,
// 全選操作
onSelectAll: (bool checked) {
_dataSource.selectAll(checked);
},
// 每頁顯示的行數
rowsPerPage: _defalutRowPageCount,
// 每頁顯示數量改變後的回調
onRowsPerPageChanged: (value) {
setState(() {
_defalutRowPageCount = value;
});
},
// 設定每頁可以顯示的行數值清單選項
availableRowsPerPage: [5, 8],
// 翻頁操作回調
onPageChanged: (value) {
print('$value');
},
// 是否升序排序
sortAscending: _sortAscending,
sortColumnIndex: _sortColumnIndex,
// 表格頭部
header: Text('Data Header'),
// 列
columns: <DataColumn>[
DataColumn(label: Text('名字')),
DataColumn(
label: Text('價格'),
// 加入排序操作
onSort: (int columnIndex, bool ascending) {
_sort<num>((Shop p) => p.price, columnIndex, ascending);
}),
DataColumn(label: Text('類型')),
],
),
],
));
}
//排序關聯_sortColumnIndex,_sortAscending
void _sort<T>(Comparable<T> getField(Shop s), int index, bool b) {
_dataSource._sort(getField, b);
setState(() {
this._sortColumnIndex = index;
this._sortAscending = b;
});
}
}
class Shop {
final String name;
final int price;
final String type;
// 預設為未選中
bool selected = false;
Shop(this.name, this.price, this.type);
}
class TableDataSource extends DataTableSource {
final List<Shop> shops = <Shop>[
Shop('name', 100, '家電'),
Shop('name2', 130, '手機'),
Shop('三星', 130, '手機'),
Shop('三星', 130, '手機'),
Shop('三星', 130, '手機'),
Shop('海信', 100, '家電'),
Shop('TCL', 100, '家電'),
];
int _selectedCount = 0;
///根據位置擷取内容行
@override
DataRow getRow(int index) {
Shop shop = shops.elementAt(index);
return DataRow.byIndex(
cells: <DataCell>[
DataCell(
Text('${shop.name}'),
placeholder: true,
),
DataCell(Text('${shop.price}'), showEditIcon: true),
DataCell(Text('${shop.type}'), showEditIcon: false),
],
selected: shop.selected,
index: index,
onSelectChanged: (bool isSelected) {
if (shop.selected != isSelected) {
_selectedCount += isSelected ? 1 : -1;
shop.selected = isSelected;
notifyListeners();
}
});
}
@override
///行數是否不确定
bool get isRowCountApproximate => false;
@override
///行數
int get rowCount => shops.length;
@override
///選中的行數
int get selectedRowCount => _selectedCount;
void selectAll(bool checked) {
for (Shop shop in shops) {
shop.selected = checked;
}
_selectedCount = checked ? shops.length : 0;
notifyListeners();
}
//排序,
void _sort<T>(Comparable<T> getField(Shop shop), bool b) {
shops.sort((Shop s1, Shop s2) {
if (!b) {
//兩個項進行交換
final Shop temp = s1;
s1 = s2;
s2 = temp;
}
final Comparable<T> s1Value = getField(s1);
final Comparable<T> s2Value = getField(s2);
return Comparable.compare(s1Value, s2Value);
});
notifyListeners();
}
}
void onTap() {
print('data onTap');
}
運作效果如圖:
4.總結
本節部落客要是給大家講解了 Flutter 的表格繪制元件的用法和特點。
- 重點掌握 DataTable 和 PaginatedDataTable 的用法。
- 實踐一下這幾個 Widget 使用方法,嘗試寫一個課程表表格頁面。