Flutter 中 GridView 的 「522 构建方式」一文让你吃透

1,712 阅读4分钟

这是我参与更文挑战的第8天,活动详情查看: 更文挑战

前言

上一篇我们聊了 Flutter 中最常用的列表组件 ListView 的 4 种构建方式,这篇我们聊聊 GridView 的 5 种构建方式2 种布局方式 以及 2 种子项构建方式, 看下面👇🏻的脑图,我们来一步一步的看实现和效果。

  • 统称为 522 方式 ,哈哈哈

image.png

第一种 - GridView 构造

先看效果

01.gif

再看源码

Flutter 好处之一就是随时可以查看有很详细注释的源码,看完源码我们就知道里面的实现了,这里先大概看看核心的内容。
image.png
这里我们只需要先关注一下 children、gridDelegate、childrenDelegate 这 3 个属性,至于这里的 SliverChildListDelegate 我们之后会聊到,这里先大概了解到我们传递过来的 children 传递到了 SliverChildListDelegate 里,那么肯定是最终通过这里布局子项的,我们继续往下看。
image.png
这里的 buildChildLayout 是不是很熟悉,上篇聊 ListView 的时候也看到了这个方法,最终子项的布局是通过这里实现的。GridView 也是同理,区别如下:

名称buildChildLayout 区别
ListView1、使用 SliverList 构建
2、只有一个 delegate 参数
GridView1、使用 SliverGrid 构建
2、有delegate、gridDelegate 两个参数

怎么使用?

看了源码我们来看看使用就简单了,只需要构建一个 List<Widget> 传入到 children 和构建一个 gridDelegate 即可。

GridView(
  // 次轴固定数量的 GridDelegate
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    // 数量设置3
    crossAxisCount: 3,
    // 主轴间距
    mainAxisSpacing: 6,
    // 次轴间距
    crossAxisSpacing: 12,
    // 子项宽高比率
    childAspectRatio: 4 / 3,
  ),
  // 构建子项,可以回看 ListView 篇
  children: List.generate(100, (index) {
    return getItem(index);
  }),
)
03.png04.png
竖屏横屏

通过上面的对比,我们可以看到无论屏幕宽度如何都有如下特性:

  • 都保持次轴上子项 数量是固定 的,最大(宽度)范围不固定
  • 间距保持一致
  • 子项宽高比一致

getItem 实现

这里很简单就是一个 Image ,如果有不懂的可以翻看 ListView 或其 我的《Flutter Widgets》专栏中对 Image 的介绍

/// 获取子项目
Widget getItem(int index) {
  var item = listData[index % 5];
  return Image.network(
    item.url,
    fit: BoxFit.cover,
  );
}

第二种 - GridView.builder 构建

这里使用 itemBuilder 来构建子项集合,gridDelegate 我们这里使用的是 最大次轴「范围」的 GridDelegate ,你可以调整最大范围值看看效果即可对比出效果,详细的使用就看代码注释吧

GridView.builder(
  // 最大次轴「范围」的 GridDelegate
  gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
    // 最大次轴宽度(或高度)
    maxCrossAxisExtent: 140,
    // 主轴间距
    mainAxisSpacing: 6,
    // 次轴间距
    crossAxisSpacing: 12,
    // 子项宽高比率
    childAspectRatio: 16 / 9,
  ),
  itemBuilder: (context, index) {
    return getItem(index);
  },
  // 子项数量
  itemCount: 100,
)

对比效果

05.png06.png
竖屏横屏

通过上面的对比,我们可以看到无论屏幕宽度如何都有如下特性:

  • 都保持次轴上子项 最大范围是固定 的,数量不固定
  • 间距保持一致
  • 子项宽高比一致


到这里我们也就对比出了 SliverGridDelegateWithFixedCrossAxisCountSliverGridDelegateWithMaxCrossAxisExtent 这两个 GridDelegate 的区别,前者范围数量固定,后者范围固定。

第三种 - GridView.count 构建

GridView.count(
  crossAxisCount: 6,
  mainAxisSpacing: 6,
  crossAxisSpacing: 12,
  childAspectRatio: 4 / 3,
  children: List.generate(100, (index) {
    return getItem(index);
  }),
)

07.png
看到这里就不用继续多说了吧,其实就是 SliverGridDelegateWithFixedCrossAxisCount 的封装呗,走我们看看源码是不是
image.png
果然和我们预想的一样,就是为了方便我们使用而封装了一下。

第四种 - GridView.extent 构建

GridView.extent(
  // 最大次轴范围
  maxCrossAxisExtent: 140,
  mainAxisSpacing: 6,
  crossAxisSpacing: 12,
  childAspectRatio: 16 / 9,
  children: List.generate(100, (index) {
    return getItem(index);
  }),
)

通过上面就可以推断出这里就是对 SliverGridDelegateWithMaxCrossAxisExtent 的封装,走看看源码验证一下
image.png
果然也是与我们预想的一样,看看效果吧
08.png

第五种 - GridView.custom 构建

最后这种看名字就是让我们自定义,其实最主要的就是自定义 gridDelegatechildrenDelegate

GridView.custom(
  gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
    maxCrossAxisExtent: 140,
    mainAxisSpacing: 6,
    crossAxisSpacing: 12,
    childAspectRatio: 16 / 9,
  ),
  childrenDelegate: SliverChildListDelegate(
    List.generate(100, (index) {
      return getItem(index);
    }),
  ),
)

这里我们回顾之前总结一下这两个参数的常用构造类

参数名称常用构建类与说明
gridDelegateSliverGridDelegateWithMaxCrossAxisExtent(次轴子项范围固定)
SliverGridDelegateWithFixedCrossAxisCount(次轴子项数量固定)
childrenDelegateSliverChildListDelegate(列表构建)
SliverChildBuilderDelegate(Builder方式构建)

深夜更文不易,如有帮助请点赞👍🏻 给予最大的支持

源码仓库

基于 Flutter 🔥 最新版本

参考链接

关注专栏

  • 此文章已收录到下面👇 的专栏,可以直接关注
  • 更多文章继续阅读|系列文章持续更新

👏 欢迎点赞➕收藏➕关注,有任何问题随时在下面👇评论,我会第一时间回复哦