ListView 学完了来学 GridView,其实这两个 Widget 的属性大同小异,并且 GridView 也和 ListView 一样,提供了好几中创建的方法:

  • 直接使用构造函数 GridView
  • 使用 GridView.builder 方法创建
  • 使用 GridView.custom 方法创建
  • 使用 GridView.count 方法创建
  • 使用 GridView.extent 方法创建

可以看到 GridView 也有 builder 和 custom 方法创建,还另外加了两种,废话不多说,一一开始吧

直接使用构造函数创建 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,
  })

你会发现,很多参数和 ListView 都是一样的:

  • scrollDirection,滚动方向。

  • reverse,滚动方向是否是阅读方向。

  • controller,滚动控制,可以设置滚动位置等。

  • primary,是否使用 widget 树中默认的 PrimaryScrollController。当滑动方向为垂直方向(scrollDirection值为 Axis.vertical)并且 controller没有指定时,primary默认为true。(这个属性我也没搞明白是干啥的,后续明白了再来修改吧)。

  • physics,列表滚动至边缘后继续拖动的物理效果,AndroidiOS 效果不同。Android 会呈现出一个波纹状(对应 ClampingScrollPhysics),而 iOS 上有一个回弹的弹性效果(对应BouncingScrollPhysics)。如果你想不同的平台上呈现各自的效果可以使用 AlwaysScrollableScrollPhysics,它会根据不同平台自动选用各自的物理效果。如果你想禁用在边缘的拖动效果,那可以使用 NeverScrollableScrollPhysics

  • shrinkWrap,该属性将决定列表的长度是否仅包裹其内容的长度。当ListView嵌在一个无限长的容器组件中时,shrinkWrap必须为true,否则Flutter会给出警告;

  • padding,列表内边距;

  • gridDelegate,这个是重点,它的作用是控制 GridView Item 如何排列,它的类型是 SliverGridDelegate,这是一个抽象类,Flutter 提供了两个它的实现类供我们使用:

    • SliverGridDelegateWithFixedCrossAxisCount
    • SliverGridDelegateWithMaxCrossAxisExtent

    稍后我们会介绍到这两个子类的区别。

  • addAutomaticKeepAlives,该属性表示是否将列表项(子组件)包裹在AutomaticKeepAlive 组件中;典型地,在一个懒加载列表中,如果将列表项包裹在AutomaticKeepAlive中,在该列表项滑出视口时它也不会被GC(垃圾回收),它会使用KeepAliveNotification来保存其状态。如果列表项自己维护其KeepAlive状态,那么此参数必须置为false

  • addRepaintBoundaries,该属性表示是否将列表项(子组件)包裹在RepaintBoundary组件中。当可滚动组件滚动时,将列表项包裹在RepaintBoundary中可以避免列表项重绘,但是当列表项重绘的开销非常小(如一个颜色块,或者一个较短的文本)时,不添加RepaintBoundary反而会更高效。和addAutomaticKeepAlive一样,如果列表项自己维护其KeepAlive状态,那么此参数必须置为false

  • addSemanticIndexes,这个是跟语音识别辅助工具有关系的。

  • cacheExtent,预渲染区域长度,ListView 会在其可视区域的两边留一个 cacheExtent 长度的区域作为预渲染区域(对于ListView.buildListView.separated构造函数创建的列表,不在可视区域和预渲染区域内的子元素不会被创建或会被销毁);

  • children,容纳子元素的组件数组。

  • semanticChildCount,真实的语义数量。

其他属性和 ListView 一样,我们就不赘述了,可以翻看之前的章节,这里只是一下 gridDelegate 参数:

SliverGridDelegateWithFixedCrossAxisCount

从名称大致就可以推测出这个是用于列数固定的情况下使用的,看一下这个类的构造函数:

  const SliverGridDelegateWithFixedCrossAxisCount({
    @required this.crossAxisCount,
    this.mainAxisSpacing = 0.0,
    this.crossAxisSpacing = 0.0,
    this.childAspectRatio = 1.0,
  })
  • crossAxisCount,每行几个
  • mainAxisSpacing,行间距
  • crossAxisSpacing,列间距
  • childAspectRatio,子元素的宽高比

偷一张图来解释这四个属性的含义,图来自 掘金 - SmallStoneSK - Flutter网格型布局 - GridView篇

四个参数的含义应该解释的很清楚了吧,下面是一个小例子:

//模拟数据的获取  
List<Widget> getList() {
    var list = new List<Widget>();
    for (var i = 0; i < 100; i++) {
      list.add(
        Container(
          alignment: Alignment.center,
          margin: EdgeInsets.all(5),
          width: 100,
          height: 100,
          color: Colors.lightGreenAccent,
          child: Text("哈哈哈${i}"),
        ),
      );
    }
    return list;
  }
//GridView
body: GridView(
    padding: EdgeInsets.all(10),
    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount:4,
        mainAxisSpacing:0,
        crossAxisSpacing:20,
        childAspectRatio:1,
    ),
    children: getList(),
)

显示效果如下:

注意一下 List 当中设置的 margin 参数。

SliverGridDelegateWithMaxCrossAxisExtent

用于 Item 宽度有限制的情况,看其构造函数:

  const 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 组件子元素的最大宽度可能是多少,然后计算得到合适的列宽(实际上,我们也很少这么应用,所以这种方法的使用频率不高)。

以上解释转自 掘金 - SmallStoneSK - Flutter网格型布局 - GridView篇

使用 GridView.builder 方法创建 GridView

还是先看源码:

  GridView.builder({
    Key key,
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
    ScrollController controller,
    bool primary,
    ScrollPhysics physics,
    bool shrinkWrap = false,
    EdgeInsetsGeometry padding,
    @required this.gridDelegate,
    @required IndexedWidgetBuilder itemBuilder,
    int itemCount,
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
    bool addSemanticIndexes = true,
    double cacheExtent,
    int semanticChildCount,
  })

builder 方法和之前的构造方法有两点不同:

  • itemBuilder
  • itemCount

这两个参数和 ListView.builder 一样,这里也不多做解释了。

使用 GridView.count 方法创建 GridView

效果和使用构造方法创建 GridView,然后 gridDelegate 设置为 SliverGridDelegateWithFixedCrossAxisCount 效果相同。

使用 GridView.extent 方法创建 GridView

效果和使用构造方法创建 GridView,然后 gridDelegate 设置为 SliverGridDelegateWithMaxCrossAxisExtent 效果相同。

Item 的点击事件

在 Flutter 中的点击事件以及其他手势处理都是由 GestureRecognizer(手势识别)GestureDetector(手势检测) 来实现的,可以看这里 延伸知识点:手势处理

results matching ""

    No results matching ""