[Flutter Package]类iOS使用方法的SectionTableView | 掘金技术征文

零、获取方式

此控件的package我已经托管到了pub仓库 若是你被墙住了,也能够看国内镜像git

使用方式就是在你的flutter pubspec.yaml中添加依赖:github

而后flutter packages get更新依赖便可

1、原由

最近写demo时发现Flutter自带的ListView widget很简陋,没有分隔线,没有section/row之分,也没有sectionHeader,若是要实现一个有分割线,有section区分,有section header的ListView,耦合会很是严重:数组

pub.dartlang.org 上没有找到封装好的这种TableView,因而乎决定本身写一个,命名为SectionTableViewbash

2、需求整理

本人是iOS开发,因此习惯了iOS上的UITableView的调用风格,因此在实现flutter的SectionTableView时,决定实现以下功能ide

  • 能够提供分割线
  • 必须提供section的数量
  • 必须提供某section内的行数(cell row)
  • 必须提供在某一section下的某一行下(indexPath)的这一行的控件(cell)
  • 能够提供某一section和头部(section header)

3、实现

为了实现这些功能,而且方便后期增长滚动功能,上下拉刷新功能,使用了StatefulWidget做为父类:函数

class SectionTableView extends StatefulWidget {
  final Widget divider;
  @required
  final int sectionCount;
  @required
  final RowCountInSectionCallBack numOfRowInSection;
  @required
  final CellAtIndexPathCallBack cellAtIndexPath;
  final SectionHeaderCallBack headerInSection;
  SectionTableView(
      {this.divider,
      this.sectionCount,
      this.numOfRowInSection,
      this.cellAtIndexPath,
      this.headerInSection});
  @override
  _SectionTableViewState createState() => new _SectionTableViewState();
}

复制代码

接着在对应的_SectionTableViewState中的build方法中,返回ListView:post

class _SectionTableViewState extends State<SectionTableView> {

  _buildCell(BuildContext context, int index) {
    //TODO: return cells/dividers/section headers
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(itemBuilder: (context, index) {
      return _buildCell(context, index);
    });
  }
}

复制代码

熟悉flutter ListView的同窗知道,ListView的builder类方法,有一个itemBuilder回调函数,参数是当前的上下文,和将要渲染的行索引index,index对应想要获取的某一行控件(cell或者叫ListItem),返回非空的组件就证实这个index有值,返回null就表示列表到尽头了。 咱们须要作的就是对index进行映射,判断当前index对应的控件,应该是列表里的section header,仍是分隔线devider,仍是某一行的真正内容cell。性能

出于性能的考虑,不可能每次调用 _buildCell的时候,都计算一遍index对应的section和row的位置,因此定义了一个类成员变量indexPathSearch,是数组,数组长度就是ListView全部的行,当 _buildCell 的参数index大于等于indexPathSearch的长度的时候,就返回null,表示列表内容到此为止了。 indexPathSearch里每个元素,就是index对应的section和row(称为indexPath),index指向实际行(cell)的时候,section和row都是大于等于0的,当section大于等于0,row==-1的时候,表示这里是一个section header,当二者都等于-1的时候,表示这里是一个分割线:优化

bool showDivider = false;
    bool showSectionHeader = false;
    if (widget.divider != null) {
      showDivider = true;
    }
    if (widget.headerInSection != null) {
      showSectionHeader = true;
    }

    for (int i = 0; i < widget.sectionCount; i++) {
      if (showSectionHeader) {
        indexPathSearch.add(IndexPath(section: i, row: -1));
      }
      int rows = widget.numOfRowInSection(i);
      for (int j = 0; j < rows; j++) {
        indexPathSearch.add(IndexPath(section: i, row: j));
        if (showDivider) {
          indexPathSearch.add(IndexPath(section: -1, row: -1));
        }
      }
    }
复制代码

计算好了index到indexPath的映射,剩下的就好说了,在_buildCell中,提取indexPath并判断indexPath的内容,返回对应的控件:ui

_buildCell(BuildContext context, int index) {
    if (index >= indexPathSearch.length) {
      return null;
    }

    IndexPath indexPath = indexPathSearch[index];
    //section header
    if (indexPath.section >= 0 && indexPath.row < 0) {
      return widget.headerInSection(indexPath.section);
    }

    if (indexPath.section < 0 && indexPath.row < 0) {
      return widget.divider;
    }

    Widget cell = widget.cellAtIndexPath(indexPath.section, indexPath.row);
    return cell;
  }
复制代码

4、 源码

library flutter_section_table_view;

import 'package:flutter/material.dart';

typedef int RowCountInSectionCallBack(int section);
typedef Widget CellAtIndexPathCallBack(int section, int row);
typedef Widget SectionHeaderCallBack(int section);

class IndexPath {
  final int section;
  final int row;
  IndexPath({this.section, this.row});
}

class SectionTableView extends StatefulWidget {
  final Widget divider;
  @required
  final int sectionCount;
  @required
  final RowCountInSectionCallBack numOfRowInSection;
  @required
  final CellAtIndexPathCallBack cellAtIndexPath;
  final SectionHeaderCallBack headerInSection;
  SectionTableView(
      {this.divider,
      this.sectionCount,
      this.numOfRowInSection,
      this.cellAtIndexPath,
      this.headerInSection});
  @override
  _SectionTableViewState createState() => new _SectionTableViewState();
}

class _SectionTableViewState extends State<SectionTableView> {
  List indexPathSearch = [];

  @override
  void initState() {
    super.initState();
    bool showDivider = false;
    bool showSectionHeader = false;
    if (widget.divider != null) {
      showDivider = true;
    }
    if (widget.headerInSection != null) {
      showSectionHeader = true;
    }

    for (int i = 0; i < widget.sectionCount; i++) {
      if (showSectionHeader) {
        indexPathSearch.add(IndexPath(section: i, row: -1));
      }
      int rows = widget.numOfRowInSection(i);
      for (int j = 0; j < rows; j++) {
        indexPathSearch.add(IndexPath(section: i, row: j));
        if (showDivider) {
          indexPathSearch.add(IndexPath(section: -1, row: -1));
        }
      }
    }
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  void didUpdateWidget(SectionTableView oldWidget) {
    super.didUpdateWidget(oldWidget);
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
  }

  _buildCell(BuildContext context, int index) {
    if (index >= indexPathSearch.length) {
      return null;
    }

    IndexPath indexPath = indexPathSearch[index];
    //section header
    if (indexPath.section >= 0 && indexPath.row < 0) {
      return widget.headerInSection(indexPath.section);
    }

    if (indexPath.section < 0 && indexPath.row < 0) {
      return widget.divider;
    }

    Widget cell = widget.cellAtIndexPath(indexPath.section, indexPath.row);
    return cell;
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(itemBuilder: (context, index) {
      return _buildCell(context, index);
    });
  }
}


复制代码

5、下一步

这是个人第一个flutter package,目前还很简陋,flutter目前尚且如此,因此你们一块儿改善它, 下一步将优化以下内容:

  • 支持滑动到指定indexPath [✓]
  • 支持滑动的时候,反馈滑动到的位置[✓]
  • 支持下拉刷新[✓]
  • 支持上拉加载[✓]
  • 支持左滑编辑

若是你们喜欢,请多多star个人项目GitHub

从 0 到 1:个人 Flutter 技术实践 | 掘金技术征文,征文活动正在进行中

相关文章
相关标签/搜索