写点什么

flutter 系列之:flutter 中常用的 GridView layout 详解

作者:程序那些事
  • 2022 年 6 月 12 日
  • 本文字数:2896 字

    阅读完需:约 10 分钟

flutter系列之:flutter中常用的GridView layout详解

简介

GridView 是一个网格化的布局,如果在填充的过程中子组件超出了展示的范围的时候,那么 GridView 会自动滚动。


因为这个滚动的特性,所以 GridView 是一个非常好用的 Widget。今天我们一起来探索一下 GridView 这个 layout 组件的秘密。

GridView 详解

GridView 是一个可滚动的 view,也就是 ScrollView,事实上 GridView 继承自 BoxScrollView:


class GridView extends BoxScrollView
复制代码


而它的父类 BoxScrollView,则是继承自 ScrollView:


abstract class BoxScrollView extends ScrollView 
复制代码


可以看到 BoxScrollView 是一个抽象类,它有两个子类,分别是今天我们要讲的 GridView 和下期要讲的 ListView。


这两个组件的区别是 GridView 是一个 2D 的布局,而 ListView 是一个线性 layout 的布局。


作为 BoxScrollView 的子类,GridView 需要实现 buildChildLayout 方法如下所示:


  @override  Widget buildChildLayout(BuildContext context) {    return SliverGrid(      delegate: childrenDelegate,      gridDelegate: gridDelegate,    );  }
复制代码


这里 GridView 返回了一个 SliverGrid,这个 SliverGrid 中有两个属性,分别是 childrenDelegate 和 gridDelegate。


其中 gridDelegate 是一个 SliverGridDelegate 的实例,用来控制子组件在 GridView 中的布局。


childrenDelegate 是一个 SliverChildDelegate 的实例,用来生成 GridView 中的子组件。


这两个属性在 GridView 的构造函数中有使用,我们接下来会详细进行讲解。

GridView 的构造函数

GridView 有很多个构造函数,首先是包含所有参数的全参数构造函数:


  GridView({    Key? key,    Axis scrollDirection = Axis.vertical,    bool reverse = false,    ScrollController? controller,    bool? primary,    ScrollPhysics? physics,    bool shrinkWrap = false,    EdgeInsetsGeometry? padding,    required this.gridDelegate,    bool addAutomaticKeepAlives = true,    bool addRepaintBoundaries = true,    bool addSemanticIndexes = true,    double? cacheExtent,    List<Widget> children = const <Widget>[],    int? semanticChildCount,    DragStartBehavior dragStartBehavior = DragStartBehavior.start,    Clip clipBehavior = Clip.hardEdge,    ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,    String? restorationId,  })
复制代码


在这个构造函数中,需要传入自定义的 gridDelegate,所以在构造函数中 gridDelegate 是 required 状态:


required this.gridDelegate
复制代码


上面提到了 GridView 中的两个自定义属性,还有一个是 childrenDelegate,这个属性是根据传入的其他参数构造而成的,如下所示:


childrenDelegate = SliverChildListDelegate(         children,         addAutomaticKeepAlives: addAutomaticKeepAlives,         addRepaintBoundaries: addRepaintBoundaries,         addSemanticIndexes: addSemanticIndexes,       ),
复制代码


另外一个 GridView 的构造函数叫做 GridView.builder,这个构造函数和默认的构造函数的区别在于 childrenDelegate 的实现不同,我们来看下 GridView.builder 中 childrenDelegate 的实现:


childrenDelegate = SliverChildBuilderDelegate(         itemBuilder,         childCount: itemCount,         addAutomaticKeepAlives: addAutomaticKeepAlives,         addRepaintBoundaries: addRepaintBoundaries,         addSemanticIndexes: addSemanticIndexes,       ),
复制代码


对比发现,GridView.builder 中的 childrenDelegate 多了两个参数,分别是 itemBuilder 和 itemCount。


那么这个两个参数是做什么用的呢?


考虑一下一个有很多 chil 的 GridView,为了提升 GridView 的展示性能,我们不可能一下取出所有的 child 元素进行构建,而是会在滚动中进行动态创建和绘制,而这里的 itemCount 就是 child 的最大容量。


而 itemBuilder 就是一个动态创建 child 的创建器,从而满足了动态创建 child 的需求。


接下来的构造函数叫做 GridView.custom,因为叫做 custom,所以这个构造函数的 SliverGridDelegate 和 SliverChildDelegate 都是可以自定义的,也就是说这两个参数都可以从外部传入,所以这两个参数都是必须的:


  required this.gridDelegate,    required this.childrenDelegate
复制代码


GirdView 还有一个构造函数叫做 GridView.count,这里的 count 是指 GridView 可以指定 cross axis 中可以包含的组件个数,所以这里的 gridDelegate 使用的是一个 SliverGridDelegateWithFixedCrossAxisCount:


gridDelegate = SliverGridDelegateWithFixedCrossAxisCount(         crossAxisCount: crossAxisCount,         mainAxisSpacing: mainAxisSpacing,         crossAxisSpacing: crossAxisSpacing,         childAspectRatio: childAspectRatio,       ),
复制代码


可以设置 crossAxisCount 的值。


最后一个 GridView 的构造函数叫做 GridView.extent,它和 count 的构造函数很类似,不过 extent 提供的是一个 maximum cross-axis extent,而不是一个固定的 count 值,所以这里的 gridDelegate 是一个 SliverGridDelegateWithMaxCrossAxisExtent 对象:


gridDelegate = SliverGridDelegateWithMaxCrossAxisExtent(         maxCrossAxisExtent: maxCrossAxisExtent,         mainAxisSpacing: mainAxisSpacing,         crossAxisSpacing: crossAxisSpacing,         childAspectRatio: childAspectRatio,       ),
复制代码


怎么理解呢?举个例子,如果 GirdView 是竖向滚动的,并且它的 width 是 400 pixels,如果这个时候 maxCrossAxisExtent 被设置为 120,那么一行只能有三列。我们可以通过调整 maxCrossAxisExtent 的值,来调整 view 的展示情况。


我们可以根据需要来选择对应的构造函数,从而满足我们不同的需求。

GridView 的使用

有了 GridView 的构造函数,GridView 使用起来就很简单了。


比如我们动态创建一个包含 image 的 child,组成一个 gridView:


class GridViewApp extends StatelessWidget{
@override Widget build(BuildContext context) { return GridView.extent( maxCrossAxisExtent: 100, padding: const EdgeInsets.all(4), mainAxisSpacing: 4, crossAxisSpacing: 4, children: buildChild(10)); }
复制代码


这里的 buildChild 用来生成一个包含 Widget 的 list,如下所示:


  List<Widget> buildChild(int number) {    return List.generate(        number, (i) => Container(        child: Image.asset('images/head.jpg')));  }
复制代码


最后将构造的 GridViewApp 放到 Scaffold 的 body 中运行:


  Widget build(BuildContext context) {
return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: GridViewApp(), ), ); }
复制代码


最后我们可以得到下面的图像:



这里我们使用的是 GridView.extent 构造函数,大家可以自行尝试其他的构造函数。

总结

GridView 是一个我们在日常工作中经常会使用的组件,希望大家能够熟练掌握。


本文的例子:https://github.com/ddean2009/learn-flutter.git

发布于: 2022 年 06 月 12 日阅读数: 14
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
flutter系列之:flutter中常用的GridView layout详解_flutter_程序那些事_InfoQ写作社区