乐趣区

Flutter网格型布局-GridView篇

1. 前言

Flutter作为时下最流行的技术之一,凭借其出色的性能以及抹平多端的差异优势,早已引起大批技术爱好者的关注,甚至一些 闲鱼 美团 腾讯 等大公司均已投入生产使用。虽然目前其生态还没有完全成熟,但身靠背后的 Google 加持,其发展速度已经足够惊人,可以预见将来对 Flutter 开发人员的需求也会随之增长。

无论是为了现在的技术尝鲜还是将来的潮流趋势,都 9102 年了,作为一个前端开发者,似乎没有理由不去尝试它。正是带着这样的心理,笔者也开始学习Flutter,同时建了一个用于练习的仓库,后续所有代码都会托管在上面,欢迎 star,一起学习。

经过上一篇对 ListView 组件的学习,我们已经对滚动型组件的使用有了初步认识,这对今天要学习的 GridView 组件十分有帮助。因为两者都继承自BoxScrollView,所以两者的属性有 80% 以上是相同的,用法非常相似。

而且如下图所示可见,GridView网格布局在 app 中的使用频率其实非常高,所以接下来就让我们来看看在 Flutter 中如何使用吧~

2. 初识 GridView

今天我们的主角 GridView 一共有 5 个构造函数:GridViewGridView.builderGridView.countGridView.extentGridView.custom。但是不用慌,因为可以说其实掌握其默认构造函数就都会了~

来看下 GridView 构造函数(已省略不常用属性):

GridView({
  Key key,
  Axis scrollDirection = Axis.vertical,
  bool reverse = false,
  ScrollController controller,
  ScrollPhysics physics,
  bool shrinkWrap = false,
  EdgeInsetsGeometry padding,
  @required this.gridDelegate,
  double cacheExtent,
  List<Widget> children = const <Widget>[],})

虽然又是一大堆属性,但是大部分都很熟悉,老朋友嘛~ 除了一个必填参数 gridDelegate 外,全和 ListView 默认构造函数的参数一样,这也是文章开头为什么说掌握了 ListView 再学 GridView 非常容易的原因。

那么接下来,就让我们来重点关注下 gridDelegate 这个参数,它其实是 GridView 组件如何控制排列子元素的一个委托。跟踪源码我们可以在 scroll_view.dart 中看到,gridDelegate的类型是 SliverGridDelegate,进一步跟踪进 sliver_grid.dart 可以看到SliverGridDelegate 其实是一个抽象类,而且一共有两个实现类:

  • SliverGridDelegateWithFixedCrossAxisCount:用于固定列数的场景;
  • SliverGridDelegateWithMaxCrossAxisExtent:用于子元素有最大宽度限制的场景;

2.1 SliverGridDelegateWithFixedCrossAxisCount

我们先来看下 SliverGridDelegateWithFixedCrossAxisCount,根据类名我们也能大概猜它是干什么用的:如果你的布局中 每一行的列数是固定的,那你就应该用它。

来看下其构造函数:

SliverGridDelegateWithFixedCrossAxisCount({
  @required this.crossAxisCount,
  this.mainAxisSpacing = 0.0,
  this.crossAxisSpacing = 0.0,
  this.childAspectRatio = 1.0,
})
  • crossAxisCount:列数,即一行有几个子元素;
  • mainAxisSpacing:主轴方向上的空隙间距;
  • crossAxisSpacing:次轴方向上的空隙间距;
  • childAspectRatio:子元素的宽高比例。

想必看到上面的示例图,你就秒懂其中各个参数的含义了。不过,这里有一点需要特别注意:如果你的子元素宽高比例不为 1,那么你一定要设置 childAspectRatio 属性

2.2 SliverGridDelegateWithMaxCrossAxisExtent

SliverGridDelegateWithMaxCrossAxisExtent在实际应用中可能会比较少,来看下其构造函数:

SliverGridDelegateWithMaxCrossAxisExtent({
  @required this.maxCrossAxisExtent,
  this.mainAxisSpacing = 0.0,
  this.crossAxisSpacing = 0.0,
  this.childAspectRatio = 1.0,
})

可以看到除了 maxCrossAxisExtent 外,其他参数和 SliverGridDelegateWithFixedCrossAxisCount 都是一样的。那么 maxCrossAxisExtent 是干什么的呢?我们来看个例子:

假如手机屏宽 375crossAxisSpacing 值为0

  • maxCrossAxisExtent值为 125 时,网格列数将是3。因为125 * 3 = 375,刚好,每一列的宽度就是375/3
  • maxCrossAxisExtent值为 126 时,网格列数将是3。因为126 * 3 > 375,显示不下,每一列的宽度将是375/3
  • maxCrossAxisExtent值为 124 时,网格列数将是4。因为124 * 3 < 375,仍有多余,每一列的宽度将是375/4

可以看到,maxCrossAxisExtent其实就是告诉 GridView 组件子元素的最大宽度可能是多少,然后计算得到合适的列宽(实际上,我们也很少这么应用,所以这种方法的使用频率不高)。

3. 实际应用

经过前面的介绍,我们已经对 GrdiView 组件有了初步了解,下面就来看看如何使用。还记得之前 GridView 的各种构造函数吗?其实:

  1. GridView默认构造函数可以类比于 ListView 默认构造函数,适用于 有限个数子元素 的场景,因为 GridView 组件会一次性全部渲染 children 中的子元素组件;
  2. GridView.builder构造函数可以类比于 ListView.builder 构造函数,适用于 长列表 的场景,因为 GridView 组件会根据子元素是否出现在屏幕内而动态创建销毁,减少内存消耗,更高效渲染;
  3. GridView.count构造函数是 GrdiView 使用 SliverGridDelegateWithFixedCrossAxisCount 的简写(语法糖),效果完全一致;
  4. GridView.extent构造函数式 GridView 使用 SliverGridDelegateWithMaxCrossAxisExtent 的简写(语法糖),效果完全一致。

先来看一个简单的例子,它使用到 GridView.count 构造函数模仿美团外卖首页服务列表(服务菜单项的代码可以看这里,也算是对基础组件使用的进一步巩固):

代码(文件地址)

GridView.count(
  crossAxisCount: 5,
  padding: EdgeInsets.symmetric(vertical: 0),
  children: serviceList.map((item) => ServiceItem(data: item)).toList(),)

/*************/
/* 完全等同于 */
/************/

GridView(padding: EdgeInsets.symmetric(vertical: 0),
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 5,),
  children: serviceList.map((item) => ServiceItem(data: item)).toList(),)

预览

再来看一个模仿喜马拉雅中相声列表用到 GridView.builder 创建网格布局的具体例子(相声卡片的代码可以看这里):

代码(文件地址)

GridView.builder(
  shrinkWrap: true,
  itemCount: programmeList.length,
  physics: NeverScrollableScrollPhysics(),
  padding: EdgeInsets.symmetric(horizontal: 16),
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 3,
    mainAxisSpacing: 10,
    crossAxisSpacing: 10,
    childAspectRatio: 0.7,
  ),
  itemBuilder: (context, index) {return Programme(data: programmeList[index]);
  },
)

预览

4. 总结

本文先是介绍了 GridView 组件的属性含义,并着重讲解了 SliverGridDelegateWithFixedCrossAxisCountSliverGridDelegateWithMaxCrossAxisExtent分别适用的应用场景。然后,通过两个实际的应用例子介绍了 GridView 组件常用的构造函数使用方法。希望通过本文的介绍,你可以掌握 Flutter 中网格型布局的使用~

本文所有代码托管在这儿,也可以关注我的 Blog,欢迎一起交流学习~

退出移动版