[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 技術實踐 | 掘金技術徵文,徵文活動正在進行中

相關文章
相關標籤/搜索