Flutter仿iOS TableView分组实现

4,679 阅读1分钟

Flutter仿iOS TableView分组实现

一、背景

Flutter的ListView或CustomScrollView支持的是单列表,无法实现分区分组,所以group_tableview来帮你实现。

二、实现原理

主要功能

功能进度及实现
分组plain,group
自定义section header支持
自定义section footer支持
能够添加刷新支持

仿iOS group样式

enum ViewStyle {
  plain, //regular table view
  group //sections are grouped together
}

GroupListView实现原理


///
/// GroupListView 实现
///
class GroupListView extends StatefulWidget {
  final ViewStyle style; //listview样式,默认是plain样式
  final int numberOfSections; //section个数 默认1
  final SectionBuilder numberOfRowsInSection; //每个section内有多少个row
  final IndexPathWidgetBuilder itemBuilder; //item builder方法
  final IndexedWidgetBuilder sectionHeaderBuilder; // section header builder 方法
  final IndexedWidgetBuilder sectionFooterBuilder; // section footer builder 方法
  final ScrollController controller; // controller
  final Color backgroundColor; // 背景颜色

  GroupListView(
      {this.itemBuilder,
      this.style = ViewStyle.plain,
      this.numberOfSections = 1,
      this.numberOfRowsInSection,
      this.sectionHeaderBuilder,
      this.sectionFooterBuilder,
      this.controller,
      this.backgroundColor})
      : assert(itemBuilder != null,"itemBuilder 不能为null");

  @override
  _GroupListViewState createState() => _GroupListViewState();
}

目前是通过ListView.builer实现

@override
  Widget build(BuildContext context) {
    _initData();
    return ListView.builder(
      itemBuilder: _itemBuilder,
      itemCount: _calculateItemCount(),
    );
  }

获取总行数,totalCount = sectionHeader+sectionFooter+rows

int _calculateItemCount() {
    return totalCount; //row+section = 总行数
  }

根据datasource初始化listview

  ///根据DataSource 初始化数据
  void _initData() {
    totalCount = 0;
    sectionList.clear();

    for (int section = 0; section < widget.numberOfSections; section++) {
      //遍历section
      int rowCount =
          widget.numberOfRowsInSection(section); //获取每个section(区)有多少个row

      Widget header; //获取header
      if (widget.sectionHeaderBuilder != null) {
        header = widget.sectionHeaderBuilder(context, section);
      }
      Widget footer; //获取footer
      if (widget.sectionFooterBuilder != null) {
        footer = widget.sectionFooterBuilder(context, section);
      }

      bool isHaveHeader = header != null ? true : false; //是否有header
      bool isHaveFooter = footer != null ? true : false; //是否有footer

      SectionModel sectionModel =
          SectionModel(section, rowCount, isHaveHeader, isHaveFooter, header);
      if (section == 0) {
        currentSectionModel = sectionModel;
      }
      sectionList.add(sectionModel);

      totalCount = totalCount +
          rowCount +
          (header != null ? 1 : 0) +
          (footer != null ? 1 : 0); //ListView itemCount总行数

      GlobalKey globalKey = GlobalKey(debugLabel: section.toString());
      keyList.add(globalKey);
    }
    listView = ListView.builder(
      controller: _controller,
      physics: BouncingScrollPhysics(),
      itemBuilder: _itemBuilder,
      itemCount: _calculateItemCount(),
    );
  }

ListView.builder渲染每一个row

  Widget _itemBuilder(BuildContext context, int index) {
    Widget item;
    Object model = _getItemRowModel(index);

    if (model is SectionHeaderModel) {
      SectionHeaderModel sectionHeaderModel = model;
      item = widget.sectionHeaderBuilder(context, sectionHeaderModel.section);
    }else if(model is SectionFooterModel){
      SectionFooterModel sectionFooterModel = model;
      item = widget.sectionFooterBuilder(context, sectionFooterModel.section);
    } else {
      RowModel rowModel = model;
      item = widget.itemBuilder(context, rowModel.indexPath);
    }
    return item;
  }

  Object _getItemRowModel(int index) {

    int passCount = 0;
    //遍历整个分区 ,去查找index在哪个分区section的哪个下标row
    for (int section = 0; section < sectionList.length; section++) {

      SectionModel sectionModel = this.sectionList[section];

      bool isHaveHeader = sectionModel.isHaveHeader;
      bool isHaveFooter = sectionModel.isHaveFooter;

      int tempCount = 0;
      if (isHaveHeader == true) {
        //有header
        tempCount = tempCount + 1;
      }

      if (index == passCount && isHaveHeader == true) {
        //这是header
        return SectionHeaderModel(section);
      }else if(index == tempCount + sectionModel.rowCount + passCount && isHaveFooter == true){
        //这是footer
        return SectionFooterModel(section);
      } else if (index >= passCount &&
          index < tempCount + sectionModel.rowCount+passCount) {
        //这是row
        IndexPath indexPath = IndexPath(section, index - passCount - tempCount);
        return RowModel(indexPath);
      }
      passCount = passCount + sectionModel.rowCount + tempCount +(isHaveFooter?1:0);
    }
    return null;
  }

效果图

源码github

已同步上传Pub搜索group_tableview

如有帮助,请给个star,谢谢😁😁😁