Loading

【Flutter】可滚动组件之GridView

前言

GridView可以构建一个二维网格列表。需要关注的是gridDelegate参数,类型是SliverGridDelegate,它的作用是控制GridView子组件如何排列(layout)。SliverGridDelegate是一个抽象类,定义了GridView Layout相关接口,子类需要通过实现它们来实现具体的布局算法。Flutter中提供了两个SliverGridDelegate的子类SliverGridDelegateWithFixedCrossAxisCount和SliverGridDelegateWithMaxCrossAxisExtent。

接口描述

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,
  })

const SliverGridDelegateWithFixedCrossAxisCount({
    @required this.crossAxisCount,
    // 主轴方向的间距
    this.mainAxisSpacing = 0.0,
    // 横轴方向子元素的间距
    this.crossAxisSpacing = 0.0,
    // 子元素在横轴长度和主轴长度的比例
    this.childAspectRatio = 1.0,
  })

const SliverGridDelegateWithMaxCrossAxisExtent({
    // 为子元素在横轴上的最大长度,之所以是“最大”长度,是因为横轴方向每个子元素的长度仍然是等分的
    @required this.maxCrossAxisExtent,
    this.mainAxisSpacing = 0.0,
    this.crossAxisSpacing = 0.0,
    // 所指的子元素横轴和主轴的长度比为最终的长度比
    this.childAspectRatio = 1.0,
  })

代码示例

// GridView

// GridView可以构建一个二维网格列表。需要关注的是gridDelegate参数,类型是SliverGridDelegate,它的作用是控制GridView子组件如何排列(layout)。
// SliverGridDelegate是一个抽象类,定义了GridView Layout相关接口,子类需要通过实现它们来实现具体的布局算法。
// Flutter中提供了两个SliverGridDelegate的子类SliverGridDelegateWithFixedCrossAxisCount和SliverGridDelegateWithMaxCrossAxisExtent。

// SliverGridDelegateWithFixedCrossAxisCount
import 'package:flutter/material.dart';

class GridViewTest1 extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    return Scaffold(
      appBar: AppBar(
        title: Text('SliverGridDelegateWithFixedCrossAxisCount'),
      ),
      body: GridView(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          // 横轴三个子widget
          crossAxisCount: 3,
          // 宽高比为1时,子widget
          childAspectRatio: 1.0
        ),
        children: <Widget>[
          Icon(Icons.ac_unit),
          Icon(Icons.airport_shuttle),
          Icon(Icons.all_inclusive),
          Icon(Icons.beach_access),
          Icon(Icons.cake),
          Icon(Icons.free_breakfast)
        ],
      ),

      // 等价于

//      body: GridView.count(
//        crossAxisCount: 3,
//        childAspectRatio: 1.0,
//        children: <Widget>[
//          Icon(Icons.ac_unit),
//          Icon(Icons.airport_shuttle),
//          Icon(Icons.all_inclusive),
//          Icon(Icons.beach_access),
//          Icon(Icons.cake),
//          Icon(Icons.free_breakfast),
//        ],
//      )

    );
  }
}

// SliverGridDelegateWithMaxCrossAxisExtent
class GridViewTest2 extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    return Scaffold(
      appBar: AppBar(
        title: Text('SliverGridDelegateWithMaxCrossAxisExtent'),
      ),
      body: GridView(
        gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
          // 元素在横轴上的最大长度
          maxCrossAxisExtent: 120.0,
          // 元素在横轴和主轴的长度比为最终的长度比
          childAspectRatio: 2.0,
        ),
        children: <Widget>[
          Icon(Icons.ac_unit),
          Icon(Icons.airport_shuttle),
          Icon(Icons.all_inclusive),
          Icon(Icons.beach_access),
          Icon(Icons.cake),
          Icon(Icons.free_breakfast)
        ],
      ),

      // 等价于

//      body: GridView.extent(
//        maxCrossAxisExtent: 120.0,
//        childAspectRatio: 2.0,
//        children: <Widget>[
//            Icon(Icons.ac_unit),
//            Icon(Icons.airport_shuttle),
//            Icon(Icons.all_inclusive),
//            Icon(Icons.beach_access),
//            Icon(Icons.cake),
//            Icon(Icons.free_breakfast)
//          ],
//      ),

    );
  }
}


// GridView.builder
// GridView都需要一个widget数组作为其子元素,这些方式都会提前将所有子widget都构建好,所以只适用于子widget数量比较少时,当子widget比较多时,我们可以通过GridView.builder来动态创建子widget
// 从一个异步数据源(如网络)分批获取一些Icon
class InfiniteGridView extends StatefulWidget{
  @override
  _InfiniteGridViewState createState() => _InfiniteGridViewState();
}

class _InfiniteGridViewState extends State<InfiniteGridView>{
  // 保存Icon数据
  List<IconData> _icons = [];

  @override
  void initState(){
    // 初始化数据
    _retrieveIcons();
  }

  @override
  Widget build(BuildContext context){

    return Scaffold(
      appBar: AppBar(
        title: Text(''),
      ),
      body: GridView.builder(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            // 每列三行
            crossAxisCount: 3,
            // 显示区域宽高相等
            childAspectRatio: 1.0,
          ),
          itemCount: _icons.length,
          itemBuilder: (context, index){
            // 如果显示到最后一个并且Icon总数小于200时继续获取数据
            if (index == _icons.length - 1 && _icons.length < 200){
              _retrieveIcons();
            }
            return Icon(_icons[index]);
          }
      ),
    );
  }

  // 模拟异步获取数据
  void _retrieveIcons(){
    Future.delayed(Duration(milliseconds: 200)).then((e){
      setState(() {
        _icons.addAll([
          Icons.ac_unit,
          Icons.airport_shuttle,
          Icons.all_inclusive,
          Icons.beach_access,
          Icons.free_breakfast,
        ]);
      });
    });
  }

}


总结

在实际开发中,可能会遇到子元素大小不等的情况,Pub上有一个包“flutter_staggered_grid_view” ,它实现了一个交错GridView的布局模型,可以很轻松的实现这种布局。

posted @ 2020-01-14 20:26  Parzulpan  阅读(1247)  评论(0编辑  收藏  举报