乐趣区

关于flutter: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

退出移动版