Code前端首页关于Code前端联系我们

Flutter中常用的GridView布局详解

terry 2年前 (2023-09-22) 阅读数 106 #移动小程序

简介

GridView是一种网格布局。如果填充过程中子组件超出显示区域,GridView会自动滚动。

由于这种滚动功能,GridView 是一个非常易于使用的小部件。今天,我们就来探究一下GridView布局组件背后的秘密。

GridView详解

GridView是一个可滚动的视图,即ScrollView。其实GridView继承自BoxScrollView:

class GridView extends BoxScrollView

而它的父类BoxScrollView继承自ScrollView:

abstract class BoxScrollView extends ScrollView 

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

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

作为BoxScrollView的子类,GridView必须实现buildChildLayout方法,如下:

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

这里GridView返回一个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 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。

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

想象一下GridView会感到很多寒意。为了提高GridView的屏幕表现,我们不能一次性把所有子元素都拿出来构建。相反,我们将在滚动期间动态创建和绘制它们,这里的 itemCount 是子项。最大容量。

ItemBuilder 是一个动态创建子项的创建器,从而满足动态创建子项的需求。

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

  required this.gridDelegate,
    required this.childrenDelegate

GirdView还有一个构造函数,叫GridView。数数。这里的count是指GridView可以指定可以包含在交叉轴中的组件数量,所以这里的gridDelegate使用了一个SliverGridDelegateWithFixedCrossAxisCount:

gridDelegate = SliverGridDelegateWithFixedCrossAxisCount(
         crossAxisCount: crossAxisCount,
         mainAxisSpacing: mainAxisSpacing,
         crossAxisSpacing: crossAxisSpacing,
         childAspectRatio: childAspectRatio,
       ),

crossAxisCount的值是可以设置的。

最后一个GridView构造函数称为GridView.extent,它与count构造函数非常相似,但extent给出的是最大横轴范围而不是固定的计数值,所以这里的gridDelegate是一个SliverGridDelegateWithMaxCrossAxisExtent对象: ♹❝ 如何你明白吗?例如,如果GirdView垂直滚动,其宽度为400像素,此时如果将maxCrossAxisExtent设置为120,那么一行中只能有三列。我们可以通过调整maxCrossAxisExtent的值来调整视图的显示。

我们可以根据自己的需求选择相应的构造函数来满足我们不同的需求。

使用GridView

有了GridView的构造函数,使用GridView就非常简单了。

比如我们动态创建一个包含图片的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));
  }

这里的构造child用于生成一个包含widget的列表,如下图:

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

最后将构造好的GridView app放入Scaffold 的 body 运行于:

  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: GridViewApp(),
      ),
    );
  }

最终我们可以得到下图:

Flutter中常用GridView layout详解

这里我们使用 GridView.extent 构造函数,您可以自己尝试其他构造函数。

概述

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

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门