flutter-GridView

659 阅读7分钟

前言

开发移动端难免会碰到各种表格视图,flutter 当然也不例外,例如: ios 的 CollectionView,android的GridView等,flutter 就有一个Gridviewflutter中就有Gridview,可展开可复用,使用极其方便,下面我们来接触一下吧

话不多说,先看一下是什么类型的是网格视图吧,至少萌新能够明白,如下图,多个表情包像网格一样排列

image.png

案例demo(grid_view目录)

GridView基本属性

其基本属性和上一篇 ListView 一样,这里就直接 copy 过来了,此外还新增了一个必填的新属性gridDelegate,其很关键

  1. scrollDirection: 列表的滚动方向,可选值有Axishorizontalvertical,可以看到默认是垂直方向上滚动;
  2. shrinkWrap: 列表都存在该属性,该属性默认为false,表示内部滚动方向无穷长,不会完全计算全部内容长度;如果该属性设置为true,会尽可能缩放滚动视图,一次性计算出所有内容,然后布局;一般嵌套滚动视图时内部设置为true,避免内部计算有问题,根据实际情况使用,一般不推荐,长列表过多计算可能会造成卡顿
  3. padding: 列表内边距;
  4. controller: 控制器,与列表滚动相关,比如监听列表的滚动事件(一般很少用);
  5. physics: 列表滚动至边缘后继续拖动的物理效果
  • AndroidiOS效果不同。默认Android会呈现出一个波纹状(对应ClampingScrollPhysics),而iOS上有一个回弹的弹性效果(对应BouncingScrollPhysics),需要设置成一样的,可以设置,注意BouncingScrollPhysics不满屏不支持滚动。
  • 如果想不同的平台上呈现各自的效果,且无论内容是否占满,都支持滚动的话,可以使用AlwaysScrollableScrollPhysics(默认也是这个,如果两端都像ios一样滚动,参数parent: BouncingScrollPhysics即可),它会根据不同平台自动选用各自的物理效果。
  • 如果想禁用在边缘的拖动效果,那可以使用NeverScrollableScrollPhysics
  1. shrinkWrap: 该属性将决定列表的长度是否仅包裹其内容的长度。当ListView嵌在一个无限长的容器组件中时,shrinkWrap必须为true,否则Flutter会给出警告;
  2. itemExtent: 子元素长度。当列表中的每一项长度是固定的情况下可以指定该值,有助于提高列表的性能(因为它可以帮助ListView在未实际渲染子元素之前就计算出每一项元素的位置);
  3. cacheExtent: 预渲染区域长度,ListView会在其可视区域的两边留一个cacheExtent长度的区域作为预渲染区域(对于ListView.buildListView.separated构造函数创建的列表,不在可视区域和预渲染区域内的子元素不会被创建或会被销毁);
  4. children: 容纳子元素的组件数组
  5. gridDelegate: 其类型为一个接口,可以穿入两个实现了该接口的类SliverGridDelegateWithFixedCrossAxisCountSliverGridDelegateWithMaxCrossAxisExtent
  • SliverGridDelegateWithFixedCrossAxisCount 固定列数,取决于crossAxisCount,用的比较多,就像 UICollectionView 一样
  • SliverGridDelegateWithMaxCrossAxisExtent:最大列宽,根据间距和最大列宽是否填充满当前行而分成多列,以最少列满足占满横轴为依据;使用最大列宽计算,略大于当前屏幕,会缩小列宽为正好满足合适宽度;如果等于就使用当前列宽;如果刚好小于最大列宽,则会再新增一列,缩小列宽占满横屏;以上逻辑不会出现单个列宽小于等于最大列宽,以牺牲部分列宽来满足填充全屏

GridView基本使用

基础GridView

基础 GridView使用,就像 ListView一样

GridView(
  //默认是垂直方向滑动,也可以竖直方向滑动,可以详细查看其它属性
  scrollDirection: Axis.vertical,
  //设置padding
  padding: const EdgeInsets.all(10),
  //设置滚动效果,这是比较常用的几个参数了
  //都和ios一样支持回弹,不填默认ios回弹,android水波纹
  physics: const AlwaysScrollableScrollPhysics(
    //两端都拥有弹性效果,不填这里和默认physics不写一样
    parent: BouncingScrollPhysics(),
  ),
  //固定列数,取决于crossAxisCount
  //用的比较多,就像UICollectionView一样,就以它为例
  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 2, //横轴有几个
    mainAxisSpacing: 10, //主轴方向间距
    crossAxisSpacing: 10, //次轴方向间距
    childAspectRatio: 1, //单个网格宽长比,默认为1,如果是长方形必填
  ),
  //最大列宽,根据间距和最大列宽是否填充满当前行而分成多列,以最少列满足占满横轴为依据
  //使用最大列宽计算,略大于当前屏幕,会缩小列宽为正好满足合适宽度
  //如果等于就使用当前列宽
  //如果刚好小于最大列宽,则会再新增一列,缩小列宽占满横屏
  //以上逻辑不会出现单个列宽小于等于最大列宽,以牺牲部分列宽来满足填充全屏
  //用的比较少
  // gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
  //   maxCrossAxisExtent: 120,
  //   mainAxisSpacing: 10, //主轴方向间距
  //   crossAxisSpacing: 10, //次轴方向间距
  // ),
  //用到这种情况是,内部不会复用,会以上面的形式来一行一行铺上
  children: [0, 1, 3, 4, 5, 6, 7, 8, 9, 10].map((e) {
    return Container(
      color: Colors.green,
    );
  }).toList(),
);

SliverGridDelegateWithFixedCrossAxisCountSliverGridDelegateWithMaxCrossAxisExtent 规则不太一样,下面简单以案例介绍一下

SliverGridDelegateWithFixedCrossAxisCount

设置 crossAxisCount 即一行固定有多少列,常见为2列,就这样较为规则的平铺下去,例如:10 张图片,每行放两个,除去间距,剩下空间平分给两个单元格视图,多出来的换行,然后继续平分,以此往复平铺

SliverGridDelegateWithMaxCrossAxisExtent

设置 maxCrossAxisExtent 最大列宽,即每个单元格视图最大这么宽,去除间距等因素,以最大列宽平分剩余空间,那么每列多少个怎么计算的呢,其以最大列宽为根据,对比当前屏幕尺寸,进行计算,如下所示

屏幕 375,边间距和网格间距都为10,那么一个单元格就占用 120,以最大列宽为基数,增加网格数量直到内容大于或者等于屏幕宽

这里以最大列宽增加到 3 倍发现达到条件,间距 40 + 120 * 3 = 400 > 375,即列宽为3

此时列宽需要缩小以避免内容出屏,列宽为减去边距除以数量: (375 - 40) / 3 ≈ 111.67 ,就得出的最适合的间距

image.png

注意:顶部是为了方便查看不同效果用的 tabbar

GridView.count

SliverGridDelegateWithFixedCrossAxisCount的语法糖

GridView.count(
  //默认是垂直方向滑动,也可以竖直方向滑动,可以详细查看其它属性
  scrollDirection: Axis.vertical,
  //设置padding
  padding: const EdgeInsets.all(10),
  //设置滚动效果,这是比较常用的几个参数了
  //都和ios一样支持回弹,不填默认ios回弹,android水波纹
  physics: const AlwaysScrollableScrollPhysics(
    //两端都拥有弹性效果,不填这里和默认physics不写一样
    parent: BouncingScrollPhysics(),
  ),
  crossAxisCount: 2,//设置列数,相当于将
  crossAxisSpacing: 10,
  mainAxisSpacing: 10,
  //用到这种情况是,内部不会复用,会以上面的形式来一行一行铺上
  children: [0, 1, 3, 4, 5, 6, 7, 8, 9, 10].map((e) {
    return Container(
      color: Colors.cyanAccent,
    );
  }).toList(),
);

image.png

GridView.extent

SliverGridDelegateWithMaxCrossAxisExtent的语法糖

GridView.extent(
  //默认是垂直方向滑动,也可以竖直方向滑动,可以详细查看其它属性
  scrollDirection: Axis.vertical,
  //设置padding
  padding: const EdgeInsets.all(10),
  //设置滚动效果,这是比较常用的几个参数了
  //都和ios一样支持回弹,不填默认ios回弹,android水波纹
  physics: const AlwaysScrollableScrollPhysics(
    //两端都拥有弹性效果,不填这里和默认physics不写一样
    parent: BouncingScrollPhysics(),
  ),
  maxCrossAxisExtent: 120,//设置最大列宽
  crossAxisSpacing: 10,
  mainAxisSpacing: 10,
  //用到这种情况是,内部不会复用,会以上面的形式来一行一行铺上
  children: [0, 1, 3, 4, 5, 6, 7, 8, 9, 10].map((e) {
    return Container(
      color: Colors.blueAccent,
    );
  }).toList(),
);

image.png

GridView.builder

GridView.builder 复用语法糖,相当于 ios 的 UICollectionView,适用于长列表

GridView.builder(
  //默认是垂直方向滑动,也可以竖直方向滑动,可以详细查看其它属性
  scrollDirection: Axis.vertical,
  //设置padding
  padding: const EdgeInsets.all(10),
  //设置滚动效果,这是比较常用的几个参数了
  //都和ios一样支持回弹,不填默认ios回弹,android水波纹
  physics: const AlwaysScrollableScrollPhysics(
    //两端都拥有弹性效果,不填这里和默认physics不写一样
    parent: BouncingScrollPhysics(),
  ),
  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 2,
    mainAxisSpacing: 10,
    crossAxisSpacing: 10,
    childAspectRatio: 0.75, //单个网格宽长比,默认为1,如果这里是垂直方向比较高
  ),
  itemBuilder: (context, index) {
    return Container(
      color: Colors.red,
    );
  },
);

image.png