一、介绍

之前我写了一篇关于 Flutter ListView Widget 的各种花里胡哨的使用,基本上能够满足日常的 ListView 使用需求,文章地址:

不过可以发现的是,我们都是在直接使用 ListView() 构造函数,实际上除了直接使用 ListView(children: List<Widget>) 的方式之外,还有 ListView.builderListView.separated 两个构造函数可以构造 ListView

二、ListView.builder() 渲染长列表数据

下面是 ListView.builder() 的注释如下:

  /// This constructor is appropriate for list views with a large (or infinite)
  /// number of children because the builder is called only for those children
  /// that are actually visible.

直接使用 ListView(children: List<Widget>) 的优点是简单边界,直接生成一个 List<Widget> 的列表,然后赋值给 ListViewchildren 参数即可

缺点是整个 ListView 是没有回收机制的,会一次性创建出所有的子项,构建完成整个 ListView。

这意味着一旦列表长度非常长,比如 无限滚动列表的场景中,列表长度非常长,直接创建 ListView 的所有子项可能会有内存问题,过高的内存会引起 Crash

如果你使用过 RN 或者是 Weex 它们都有 RecycleList 的概念。

本身 ListView 是继承自 BoxScrollView 继承自 ScrollView 实现的,而如果直接使用 ListView 的构造函数,传递给 SliverChildBuilderDelegate 的是 children,而如果使用 ListView.builder 传递给 SliverChildBuilderDelegate 这是 builder,这里不详细展开。

ListView.builder 接受两个参数:

  • itemCount: int
  • itemBuilder: @required IndexedWidgetBuilder itemBuilder,

1、ListView.builder 的参数 itemCount

itemCount 这个参数声明了 list 的长度,这个值表明 ListView 中会存在多少个 item 子项

2、ListView.builder 的参数 itemBuilder

从 itemBuilder 的表面含以上也能够知道它是用来构造每个子项组件的,它的类型是 IndexedWidgetBuilder,其实是一个方法,接受 context 和 index 两个参数。

  • context:构造的上下文
  • index: 当前索引

比如我的 itemBuilder 方法如下:

其实非常简单,从 list 中通过 list[index] 拿到当前渲染的数据,然后返回一个 Widget 即可

  // item build 方法
  Widget _buildListItem(BuildContext context, int index) {
    Map newsItem = newsList.news[index];
    return ListItem(
        title: newsItem['title'],
        subTitle: newsItem['time'],
        cover: newsItem['imgurl']);
  }

3、完整的 ListView.builder 渲染

省略了 ListItem 的代码,完整代码可以在文章最后找到

class HomeContent extends StatelessWidget {
  // item build 方法
  Widget _buildListItem(BuildContext context, int index) {
    Map newsItem = newsList.news[index];
    return ListItem(
        title: newsItem['title'],
        subTitle: newsItem['time'],
        cover: newsItem['imgurl']);
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: newsList.news.length,
      itemBuilder: this._buildListItem,
    ); 
  }
}

最终的效果:

4.gif

三、ListView.separated 带分隔符的列表

上面的列表中也可以发现,我们的列表是没有分隔符的,虽然我们可以在每个 ListTile 中增加手动增加一个分隔符,但是 Flutter 的 ListView 已经给我们提供了便捷的方法。

ListView.separatedListView.builder 的唯一不同就是多了个 separatorBuilder,类型也是 IndexedWidgetBuilder,其实也就是通过接受 BuildContext context, int index 两个参数,再返回一个 Widget

其他的和 ListView.builder 一样使用

比如下面我的代码:

省略了 this._buildListItem 的代码,和上面一样,完整代码在文章最后

  // 能够生成下划线的列表
  @override
  Widget build(BuildContext context) {
    return ListView.separated(
      itemCount: newsList.news.length,
      itemBuilder: this._buildListItem,
      separatorBuilder: (BuildContext context, int index) {
        return index % 2 == 0
            ? Divider(color: Colors.blue)
            : Divider(color: Colors.red);
      },
    ); // 接收的两个参数
  }

上面代码中 我通过 index 控制了偶数蓝色分割线,奇数是红色分割线,最终效果:

60753-vie4zaz7ka.png

当然,separatorBuilder 不一定只能返回 Divider,只要是 Widget 就可以,比如我通过一个 Icon 作为分隔符:

      separatorBuilder: (BuildContext context, int index) {
        return index % 2 == 0
            ? Icon(Icons.arrow_drop_down)
            : Icon(Icons.arrow_downward);
      },

75029-mx9st4h3mkh.png

四、完整代码:

ListView.builder() 代码:

ListView.separated() 代码: