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


    简介

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

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

    GridView详解

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

    class GridView extends BoxScrollView
    
    • 1

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

    abstract class BoxScrollView extends ScrollView 
    
    • 1

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

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

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

      @override
      Widget buildChildLayout(BuildContext context) {
        return SliverGrid(
          delegate: childrenDelegate,
          gridDelegate: gridDelegate,
        );
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这里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 children = const [],
        int? semanticChildCount,
        DragStartBehavior dragStartBehavior = DragStartBehavior.start,
        Clip clipBehavior = Clip.hardEdge,
        ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
        String? restorationId,
      })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

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

    required this.gridDelegate
    
    • 1

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

    childrenDelegate = SliverChildListDelegate(
             children,
             addAutomaticKeepAlives: addAutomaticKeepAlives,
             addRepaintBoundaries: addRepaintBoundaries,
             addSemanticIndexes: addSemanticIndexes,
           ),
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    childrenDelegate = SliverChildBuilderDelegate(
             itemBuilder,
             childCount: itemCount,
             addAutomaticKeepAlives: addAutomaticKeepAlives,
             addRepaintBoundaries: addRepaintBoundaries,
             addSemanticIndexes: addSemanticIndexes,
           ),
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    对比发现,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
    
    • 1
    • 2

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

    gridDelegate = SliverGridDelegateWithFixedCrossAxisCount(
             crossAxisCount: crossAxisCount,
             mainAxisSpacing: mainAxisSpacing,
             crossAxisSpacing: crossAxisSpacing,
             childAspectRatio: childAspectRatio,
           ),
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可以设置crossAxisCount的值。

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

    gridDelegate = SliverGridDelegateWithMaxCrossAxisExtent(
             maxCrossAxisExtent: maxCrossAxisExtent,
             mainAxisSpacing: mainAxisSpacing,
             crossAxisSpacing: crossAxisSpacing,
             childAspectRatio: childAspectRatio,
           ),
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    怎么理解呢?举个例子,如果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));
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

      List buildChild(int number) {
        return List.generate(
            number, (i) => Container(
            child: Image.asset('images/head.jpg')));
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

      Widget build(BuildContext context) {
    
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: GridViewApp(),
          ),
        );
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

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

    总结

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

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

  • 相关阅读:
    redis分布式锁的实现
    leetcode算法每天一题027:移除元素
    python小技巧:创建单链表及删除元素
    2. PCIE TLP解包封包
    ctf-pikachu-file_download
    vben admin BasicTable 表格的基本使用
    2023/4/30周报
    (电力开发)376.1 主站通信协议基本结构解析
    考虑关系的图卷积神经网络R-GCN的一些理解以及DGL官方代码的一些讲解
    基于Google‘s FCM实现消息推送
  • 原文地址:https://blog.csdn.net/superfjj/article/details/126837012