Flutter 132: 图解 PaginatedDataTable 分页表格

简介: 0 基础学习 Flutter,第一百三十二步:简单学习 PaginatedDataTable 分页表格!

    小菜在尝试列表展示时,对于固定类型数据库表展示需要支持左右滑动,了解到 PaginatedDataTable 分页表格,学习一下设计思路;

PaginatedDataTable

源码分析

PaginatedDataTable({
    Key key,
    @required this.header,          // 表格标题
    this.actions,                   // 标题右侧图标按钮
    @required this.columns,         // 表格表头
    this.sortColumnIndex,           // 表格索引
    this.sortAscending = true,      // 升序降序
    this.onSelectAll,               // 全选回调
    this.dataRowHeight = kMinInteractiveDimension,  // 表格行高
    this.headingRowHeight = 56.0,   // 标题高度
    this.horizontalMargin = 24.0,   // 表格外边距
    this.columnSpacing = 56.0,      // 单元格间距
    this.showCheckboxColumn = true, // 多选框显隐性
    this.initialFirstRowIndex = 0,  // 初始化起始索引
    this.onPageChanged,             // 页面切换回调
    this.rowsPerPage = defaultRowsPerPage,          // 每页数据条数
    this.availableRowsPerPage = const <int>[defaultRowsPerPage, defaultRowsPerPage * 2, defaultRowsPerPage * 5, defaultRowsPerPage * 10],   // 每页行数变更列表
    this.onRowsPerPageChanged,      // 每页数据条数变更回调
    this.dragStartBehavior = DragStartBehavior.start,
    @required this.source,          // 数据来源
})

    简单分析源码可得,PaginatedDataTable 是由 DataTable 延伸而来的,并被 Card 包裹;区别在于 PaginatedDataTable 支持分页展示;

    小菜将分页表单分为五部分,分别是 DataTable 整体数据表格、DataColumn 横向数据表头、DataRow 纵向数据列表、DataCell 数据表单元格以及 DataTableSource 数据来源;

    而 PaginatedDataTable 分页数据表格也是通过 Columnheader 标题与 DataTable 数据表格以及 footer 分页按钮等封装在一起的;

案例尝试

1. header & columns & source

    header & columns & source 作为基本 PaginatedDataTable 三个必要属性;其中 header 作为表格的标题,不可为空,建议常用的是 Text 也可以用 ButtonBar 按钮容器,日常其他 Widget 也是可以的;

    columns 作为数据表头,是一个 DataColumn 列表,其中列表长度应与 source 资源列表数组长度一致,通过 label 来展示表头信息,也可以通过 onSort 回调来进行列表排序监听;

    source 是来自 DataTableSource 类的数据源;主要实现四个抽象方法,分别是 getRow() 根据索引获取行内容、rowCount 数据源行数、isRowCountApproximate 行数是否确定以及 selectedRowCount 选中的行数(并非选中数组而是选中数量);

class _PaginatedPageState extends State<PaginatedDataTablePage> {
  DataTableSource _sourceData = SourceData();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(title: Text('PaginatedDataTable Page')),
            body: Column(children: [
              SizedBox(height: 10),
              PaginatedDataTable(
                  source: _sourceData,
                  header: Text('Flight Products'),
                  columns: [
                    DataColumn(label: Text('Avatar')),
                    DataColumn(label: Text('ID')),
                    DataColumn(label: Text('Name')),
                    DataColumn(label: Row(children: [ Text('Price'), SizedBox(width: 5.0), Icon(Icons.airplanemode_active) ])),
                    DataColumn(label: Text('No.')),
                    DataColumn(label: Text('Address'))
                  ])
            ])));
  }
}

class SourceData extends DataTableSource {
  final List<Map<String, dynamic>> _sourceData = List.generate(
      200, (index) => {
            "avatar": (index % 3 == 1) ? 'images/icon_hzw01.jpg' : (index % 3 == 2) ? 'images/icon_hzw03.jpg' : 'images/icon_music.png',
            "id": (index + 1),
            "name": "Item Name ${(index + 1)}",
            "price": Random().nextInt(10000),
            "no.": Random().nextInt(10000),
            "address": (index % 3 == 1) ? 'Beijing' : (index % 3 == 2) ? 'New York' : 'Los Angeles'
          });

  bool get isRowCountApproximate => false;

  int get rowCount => _sourceData.length;

  int get selectedRowCount => 0;

  DataRow getRow(int index) => DataRow(cells: [
        DataCell(CircleAvatar(backgroundImage: AssetImage(_sourceData[index]["avatar"]))),
        DataCell(Text(_sourceData[index]['id'].toString())),
        DataCell(Text(_sourceData[index]['name'])),
        DataCell(Text('\$ ${_sourceData[index]['price']}')),
        DataCell(Text(_sourceData[index]['no.'].toString())),
        DataCell(Text(_sourceData[index]['address'].toString()))
      ]);
}

2. actions & headingRowHeight

    数据表的标题内容主要是通过 header 展示,而源码标题是一个 Row 结构,可以通过 actions 在右侧添加 IconWidget,类似于 ToolBar;还可以通过 headingRowHeight 调整标题行的整体高度,默认是 56.0

header: Text('Flight Products'),
actions: [Icon(Icons.refresh), Icon(Icons.clear)],
headingRowHeight: 80.0,

3. dataRowHeight & horizontalMargin & columnSpacing

    dataRowHeight 为数据元素行高,默认为 48.0horizontalMargin 为表格首列和尾列外边距,默认为 24.0columnSpacing 为单元格间距,默认为 56.0

dataRowHeight: 60.0,
horizontalMargin: 40.0,
columnSpacing: 80.0,

4. rowsPerPage & initialFirstRowIndex & onPageChanged

    rowsPerPage 为每页展示数据条数,默认为 10onPageChanged 为页面左右切换时回调,回调结果为数据索引值;initialFirstRowIndex 为初始化展示索引位置,注意,若前置数据条数不满足整数页时,取整数页前一页;

rowsPerPage: 9,
initialFirstRowIndex: 20,
onPageChanged: (i) => print('onPageChanged -> $i'),

5. availableRowsPerPage & onRowsPerPageChanged

    onRowsPerPageChanged 不为空时可以设置左下角每页展示行数;此时 availableRowsPerPage 列表不可为空,且小菜测试,列表首个元素需要与初始化的行数一致;

var _rowsPerPage = 8;

rowsPerPage: _rowsPerPage,
availableRowsPerPage: [8, 16, 20],
onRowsPerPageChanged: (value) => setState(() => _rowsPerPage = value),

6. sortAscending & sortColumnIndex

    sortAscending 用于设置表格数据升序还是降序,需要配合 DataColumn 中的 onSort() 回调共同使用;sortColumnIndex 对应可升序降序的表头数组下标;

PaginatedDataTable(
    source: _sourceData,
    header: Text('Flight Products'),
    actions: [Icon(Icons.refresh), Icon(Icons.clear)],
    headingRowHeight: 50.0,
    dataRowHeight: 60.0,
    rowsPerPage: _rowsPerPage,
    onPageChanged: (i) => print('onPageChanged -> $i'),
    availableRowsPerPage: [8, 16, 20],
    onRowsPerPageChanged: (value) => setState(() => _rowsPerPage = value),
    sortAscending: _sortAscending,
    sortColumnIndex: 1,
    columns: [
      DataColumn(label: Text('Avatar')),
      DataColumn(
          label: Text('ID'),
          onSort: (index, sortAscending) {
            setState(() {
              _sortAscending = sortAscending;
              _sourceData.sortData((map) => map['id'], sortAscending);
            });
          }),
      DataColumn(label: Text('Name')),
      DataColumn(label: Row(children: [Text('Price'), SizedBox(width: 5.0), 
Icon(Icons.airplanemode_active) ])),
      DataColumn(label: Text('No.')),
      DataColumn(label: Text('Address'))
    ])

void sortData<T>(Comparable<T> getField(Map<String, dynamic> map), bool b) {
  _sourceData.sort((Map<String, dynamic> map1, Map<String, dynamic> map2) {
    if (!b) {
      //两个项进行交换
      final Map<String, dynamic> temp = map1;
      map1 = map2;
      map2 = temp;
    }
    final Comparable<T> s1Value = getField(map1);
    final Comparable<T> s2Value = getField(map2);
    return Comparable.compare(s1Value, s2Value);
  });
  notifyListeners();
}

7. showCheckboxColumn & onSelectAll

    showCheckboxColumn 用于多选框显隐性,其前提是 DataTableSource 数据源中 DataRow 设置了 selected 属性;onSelectAll 为全选时回调,状态需要自己更新;

showCheckboxColumn: true,
onSelectAll: (state) => setState(() => _sourceData.selectAll(state)),

DataRow getRow(int index) => DataRow.byIndex(
        index: index,
        selected: _sourceData[index]["selected"],
        onSelectChanged: (selected) {
          _sourceData[index]["selected"] = selected;
          notifyListeners();
        },
        cells: [
          DataCell(CircleAvatar(backgroundImage: AssetImage(_sourceData[index]["avatar"]))),
          DataCell(Text(_sourceData[index]['id'].toString())),
          DataCell(Text(_sourceData[index]['name'])),
          DataCell(Text('\$ ${_sourceData[index]['price']}')),
          DataCell(Text(_sourceData[index]['no.'].toString())),
          DataCell(Text(_sourceData[index]['address'].toString()))
        ]);

void selectAll(bool checked) {
  _sourceData.forEach((data) => data["selected"] = checked);
  _selectCount = checked ? _sourceData.length : 0;
  notifyListeners(); //通知监听器去刷新
}


    PaginatedDataTable 案例源码


    PaginatedDataTable 主要是通过 RenderObject 进行 applyPaintTransform 逐个计算单元格位置;小菜对其尝试还不够深入,如有错误,请多多指导!

来源: 阿策小和尚
目录
相关文章
|
9月前
flutter开发中Use ‘const’ with the constructor to improve performance. Try adding the ‘const’ keyword to the constructor invocation.报错如何解决-优雅草卓伊凡
flutter开发中Use ‘const’ with the constructor to improve performance. Try adding the ‘const’ keyword to the constructor invocation.报错如何解决-优雅草卓伊凡
113 1
|
8月前
|
前端开发 安全 开发工具
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
396 90
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
9月前
|
Dart 前端开发
【05】flutter完成注册页面完善样式bug-增加自定义可复用组件widgets-严格规划文件和目录结构-规范入口文件-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【05】flutter完成注册页面完善样式bug-增加自定义可复用组件widgets-严格规划文件和目录结构-规范入口文件-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
284 75
【05】flutter完成注册页面完善样式bug-增加自定义可复用组件widgets-严格规划文件和目录结构-规范入口文件-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
8月前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
459 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
9月前
|
Dart 前端开发 容器
【07】flutter完成主页-完成底部菜单栏并且做自定义组件-完整短视频仿抖音上下滑动页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【07】flutter完成主页-完成底部菜单栏并且做自定义组件-完整短视频仿抖音上下滑动页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
244 18
【07】flutter完成主页-完成底部菜单栏并且做自定义组件-完整短视频仿抖音上下滑动页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
9月前
|
前端开发 Java 开发工具
【03】完整flutter的APP打包流程-以apk设置图标-包名-签名-APP名-打包流程为例—-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈 章节内容【03】
【03】完整flutter的APP打包流程-以apk设置图标-包名-签名-APP名-打包流程为例—-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈 章节内容【03】
536 18
【03】完整flutter的APP打包流程-以apk设置图标-包名-签名-APP名-打包流程为例—-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈 章节内容【03】
|
8月前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
183 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
7月前
|
索引
【Flutter 开发必备】AzListView 组件全解析,打造丝滑索引列表!
在 Flutter 开发中,AzListView 是实现字母索引分类列表的理想选择。它支持 A-Z 快速跳转、悬浮分组标题、自定义 UI 和高效性能,适用于通讯录、城市选择等场景。本文将详细解析 AzListView 的核心参数和实战示例,助你轻松实现流畅的索引列表。
252 7
|
9月前
|
缓存 前端开发 Android开发
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
378 12
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
|
9月前
|
Dart 前端开发 架构师
【01】vs-code如何配置flutter环境-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈-供大大的学习提升
【01】vs-code如何配置flutter环境-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈-供大大的学习提升
440 26