天天看點

Flutter學習記錄——12.表格元件1.Table Widget2.DataTable Widget3.PaginatedDataTable Widget4.總結

文章目錄

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

運作效果如圖:

Flutter學習記錄——12.表格元件1.Table Widget2.DataTable Widget3.PaginatedDataTable Widget4.總結

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

運作效果如圖:

Flutter學習記錄——12.表格元件1.Table Widget2.DataTable Widget3.PaginatedDataTable Widget4.總結

4.總結

本節部落客要是給大家講解了 Flutter 的表格繪制元件的用法和特點。

  • 重點掌握 DataTable 和 PaginatedDataTable 的用法。
  • 實踐一下這幾個 Widget 使用方法,嘗試寫一個課程表表格頁面。