文章已同步至【个人博客】,欢迎访问【我的主页】😃
文章地址:blog.fanjunyang.zone/archives/fl…
前面我们说了—Flutter 滚动控件篇–>ListView
这里说一下GridView
,他和ListView
的不同之处在于,GridView
可以构建一个二维网格列表。
顺便再说一下CustomScrollView
GridView
源码示例
构造函数如下:
GridView({
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
@required SliverGridDelegate gridDelegate, //控制子widget layout的委托
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
double cacheExtent,
List<Widget> children = const <Widget>[],
})
属性解释
可以看到,GridView
的大部分属性都和ListView
一样,并且他们的含义也都相同。如果不知道的话,请点击:Flutter 滚动控件篇–>ListView
gridDelegate
这里只说一下gridDelegate
,他的类型是SliverGridDelegate
,作用是控制GridView
的子组件如何排列(layout)。
SliverGridDelegate
是一个抽象类,Flutter中提供了两个SliverGridDelegate
的子类SliverGridDelegateWithFixedCrossAxisCount
和SliverGridDelegateWithMaxCrossAxisExtent
,我们可以直接使用,下面来介绍一下它们。
SliverGridDelegateWithFixedCrossAxisCount
该子类实现了一个横轴为固定数量子元素的layout算法,
源码示例
构造函数如下:
SliverGridDelegateWithFixedCrossAxisCount({
@required double crossAxisCount,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
double childAspectRatio = 1.0,
})
属性解释
crossAxisCount
该属性表示横轴子元素的数量。
此属性值确定后子元素在横轴的长度就确定了,即ViewPort横轴长度除以crossAxisCount的商。
mainAxisSpacing
该属性表示主轴方向的间距。
crossAxisSpacing
表示横轴方向子元素的间距。
childAspectRatio
表示子元素在横轴长度和主轴长度的比例。
由于crossAxisCount
指定后,子元素横轴长度就确定了,然后通过此参数值就可以确定子元素在主轴的长度。
代码示例
GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, //横轴三个子widget
childAspectRatio: 1.0 //宽高比为1时,子widget
),
children: <Widget>[
Icon(Icons.ac_unit),
Icon(Icons.airport_shuttle),
Icon(Icons.all_inclusive,color: Colors.red,),
Icon(Icons.beach_access),
Icon(Icons.cake),
Icon(Icons.free_breakfast)
])
运行效果:
GridView.count
GridView.count
构造函数内部使用了SliverGridDelegateWithFixedCrossAxisCount
,我们通过它可以快速的创建横轴固定数量子元素的GridView
,
上面的示例代码等价于:
GridView.count(
crossAxisCount: 3,
childAspectRatio: 1.0,
children: <Widget>[
Icon(Icons.ac_unit),
Icon(Icons.airport_shuttle),
Icon(Icons.all_inclusive,color: Colors.red,),
Icon(Icons.beach_access),
Icon(Icons.cake),
Icon(Icons.free_breakfast),
],
)
SliverGridDelegateWithMaxCrossAxisExtent
该子类实现了一个横轴子元素为固定最大长度的layout算法,
源码示例
构造函数如下:
SliverGridDelegateWithMaxCrossAxisExtent({
double maxCrossAxisExtent,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
double childAspectRatio = 1.0,
})
属性解释
maxCrossAxisExtent
maxCrossAxisExtent
为子元素在横轴上的最大长度,之所以是“最大”长度,是因为横轴方向每个子元素的长度仍然是等分的。
举个例子,如果ViewPort
的横轴长度是450,那么当maxCrossAxisExtent的值在区间[450/4,450/3)
内的话,子元素最终实际长度都为112.5,而childAspectRatio
所指的子元素横轴和主轴的长度比为最终的长度比。
其他的参数和SliverGridDelegateWithFixedCrossAxisCount
相同。
代码示例
GridView(
padding: EdgeInsets.zero,
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 100.0, //其中横轴排列组件的数量与这个值有关
childAspectRatio: 2.0 //宽高比为2
),
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.extent
GridView.extent
构造函数内部使用了SliverGridDelegateWithMaxCrossAxisExtent
,我们通过它可以快速的创建纵轴子元素为固定最大长度的的GridView
。
上面的示例代码等价于:
GridView.extent(
maxCrossAxisExtent: 100.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
和ListView
一样,children
只适合子组件较少的情况下,
而当子组件较多时,我们可以通过GridView.builder
来动态创建子组件。
源码示例
构造函数如下:
GridView.builder(
...
@required SliverGridDelegate gridDelegate,
@required IndexedWidgetBuilder itemBuilder,
)
属性解释
其中gridDelegate
属性在上面说过,这里只有一个itemBuilder
,其实itemBuilder
就是子组件的构建器。
代码示例
假设我们需要从一个异步数据源(如网络)分批获取一些Icon
,然后用GridView
来展示:
import 'package:flutter/material.dart';
class CategoryPage extends StatefulWidget {
@override
_CategoryPageState createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
List<IconData> _icons = []; //保存Icon数据
@override
void initState() {
// 初始化数据
_retrieveIcons();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("GridView")),
body: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, //每行三列
childAspectRatio: 1.0 //显示区域宽高相等
),
itemCount: _icons.length,
itemBuilder: (context, index) {
//如果显示到最后一个并且Icon总数小于100时继续获取数据
if (index == _icons.length - 1 && _icons.length < 100) {
_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.cake,
Icons.free_breakfast
]);
});
});
}
}
_retrieveIcons()
:在此方法中我们通过Future.delayed
来模拟从异步数据源获取数据,每次获取数据需要200毫秒,获取成功后将新数据添加到_icons
,然后调用setState
重新构建。
在itemBuilder
中,如果显示到最后一个时,判断是否需要继续获取数据,然后返回一个Icon
。
运行效果:
补充
Flutter 的GridView
默认子元素显示空间是相等的,但在实际开发中,你可能会遇到子元素大小不等的情况。
如图:
这时候,在pub.dev上面有个包flutter_staggered_grid_view
,它实现了一个交错GridView
的布局模型,可以很轻松的实现这种布局。
CustomScrollView
CustomScrollView
是可以使用Sliver
来自定义滚动模型(效果)的组件,它可以包含多种滚动模型。
假设有一个页面,顶部需要一个GridView
,底部需要一个ListView
,而要求整个页面的滑动效果是统一的,即它们看起来是一个整体。如果使用GridView+ListView
来实现的话,就不能保证一致的滑动效果,因为它们的滚动效果是分离的,所以这时就需要一个”胶水”,把这些彼此独立的可滚动组件”粘”起来,而CustomScrollView
的功能就相当于“胶水”。
可滚动组件的Sliver版
Sliver在前面讲过,有细片、薄片之意,在Flutter中,Sliver通常指可滚动组件子元素(就像一个个薄片一样)。但是在CustomScrollView
中,需要粘起来的可滚动组件就是CustomScrollView
的Sliver了。
如果直接将ListView
、GridView
作为CustomScrollView
是不行的,因为它们本身是可滚动组件而并不是Sliver!
因此,为了能让可滚动组件能和CustomScrollView
配合使用,Flutter提供了一些可滚动组件的Sliver版,如SliverList
、SliverGrid
等。
实际上Sliver版的可滚动组件和非Sliver版的可滚动组件最大的区别就是前者不包含滚动模型(子身不能再滚动),而后者包含滚动模型 ,也正因如此,CustomScrollView
才可以将多个Sliver”粘”在一起,这些Sliver共用CustomScrollView
的Scrollable
,所以最终才实现了统一的滑动效果。
需要注意一点:CustomScrollView
的子组件必须都是Sliver。
代码示例
import 'package:flutter/material.dart';
class CategoryPage extends StatefulWidget {
@override
_CategoryPageState createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
@override
Widget build(BuildContext context) {
return Material(
child: CustomScrollView(
slivers: <Widget>[
//AppBar,包含一个导航栏
SliverAppBar(
pinned: true,
expandedHeight: 250.0,
flexibleSpace: FlexibleSpaceBar(
title: const Text('Demo'),
background: Image.asset(
"images/Test.jpg",
fit: BoxFit.cover,
),
),
),
SliverPadding(
padding: const EdgeInsets.all(8.0),
sliver: new SliverGrid(
//Grid
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, //Grid按两列显示
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) {
//创建子widget
return new Container(
alignment: Alignment.center,
color: Colors.cyan[100 * (index % 9)],
child: new Text('grid item $index'),
);
},
childCount: 20,
),
),
),
//List
new SliverFixedExtentList(
itemExtent: 50.0,
delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) {
//创建列表项
return new Container(
alignment: Alignment.center,
color: Colors.lightBlue[100 * (index % 9)],
child: new Text('list item $index'),
);
}, childCount: 50 //50个列表项
),
),
],
),
);
}
}
代码分为三部分:
-
头部
SliverAppBar
:SliverAppBar
对应AppBar
,两者不同之处在于SliverAppBar
可以集成到CustomScrollView
。SliverAppBar
可以结合FlexibleSpaceBar
实现Material Design
中头部伸缩的模型。 -
中间的
SliverGrid
:它用SliverPadding
包裹以给SliverGrid
添加补白。SliverGrid
是一个两列,宽高比为4的网格,它有20个子组件。 -
底部
SliverFixedExtentList
:它是一个所有子元素高度都为50像素的列表。
运行效果:
X_X
今天的文章Flutter 滚动控件篇–>GridView、CustomScrollView分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/18944.html