Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flutter网格型布局 - GridView篇 #15

Open
SmallStoneSK opened this issue Jul 14, 2019 · 0 comments
Open

Flutter网格型布局 - GridView篇 #15

SmallStoneSK opened this issue Jul 14, 2019 · 0 comments
Labels
Flutter Flutter学习

Comments

@SmallStoneSK
Copy link
Owner

1. 前言

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

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

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

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

app中网格布局的使用

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中网格型布局的使用~

本文所有代码托管在这儿,欢迎一起交流学习~

@SmallStoneSK SmallStoneSK added the Flutter Flutter学习 label Jul 14, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Flutter Flutter学习
Projects
None yet
Development

No branches or pull requests

1 participant