https://api.flutter.dev/flutter/widgets/GridView-class.html
当数据量很大的时候用矩阵方式排列比较清晰。此时我们可以用网格列表组件GridView 实现布局。
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>[],
})
gridDelegate
类型是 SliverGridDelegate
,作用是控制子控件的排列。
SliverGridDelegate
是一个抽象类,定义了GridView
Layout相关接口,子类需要通过实现它们来实现具体的布局算法。有2个选择:
SliverGridDelegateWithFixedCrossAxisCount
:交叉轴方向上固定数量,对于垂直方向的GridView来说交叉轴方向指的是水平方向。SliverGridDelegateWithMaxCrossAxisExtent
:交叉轴方向上尽量大,比如水平方上有500空间,指定此值为150,那么可以放3个,剩余一些空间,此时GridView将会缩小每一个Item,放置4个。
SliverGridDelegateWithFixedCrossAxisCount
该子类实现了一个横轴为固定数量子元素的layout算法,其构造函数为:
SliverGridDelegateWithFixedCrossAxisCount({
@required double crossAxisCount,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
double childAspectRatio = 1.0,
})
mainAxisSpacing
主轴方向的间距。crossAxisSpacing
交叉轴方向上之间的间隔。crossAxisCount
交叉轴方向上子元素的数量。childAspectRatio
子控件宽高比。
示例
GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 5.0,
mainAxisSpacing: 10.0,
childAspectRatio: 16 / 5,
),
children: List.generate(
10,
(index) => Container(
color: Colors.primaries[index % Colors.primaries.length],
child: Text('数据 $index'),
),
),
),
GridView.count
GridView.count
构造函数内部使用了SliverGridDelegateWithFixedCrossAxisCount
,我们通过它可以快速的创建横轴固定数量子元素的GridView
,上面的示例等同于:
GridView.count(
crossAxisCount: 3,
crossAxisSpacing: 5.0,
mainAxisSpacing: 10.0,
childAspectRatio: 16 / 5,
// children: ...,
),
SliverGridDelegateWithMaxCrossAxisExtent
该子类实现了一个横轴子元素为固定最大长度的layout算法,其构造函数为:
SliverGridDelegateWithMaxCrossAxisExtent({
double maxCrossAxisExtent,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
double childAspectRatio = 1.0,
})
maxCrossAxisExtent
为子元素在横轴上的最大长度,之所以是“最大”长度,是因为主轴方向每个子元素的长度仍然是等分的。
示例
如果ViewPort的横轴长度是480,那么当maxCrossAxisExtent
的值在区间[480/4,480/3)内的话,子元素最终实际长度都为 480 / 4。
GridView(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
crossAxisSpacing: 5.0,
mainAxisSpacing: 10.0,
childAspectRatio: 16 / 5,
),
children: List.generate(
10,
(index) => Container(
color: Colors.primaries[index % Colors.primaries.length],
child: Text('数据 $index'),
),
),
),
GridView.extent
GridView.extent构造函数内部使用了SliverGridDelegateWithMaxCrossAxisExtent,我们通过它可以快速的创建纵轴子元素为固定最大长度的的GridView,上面的示例代码等价于:
GridView.extent(
maxCrossAxisExtent: 150,
crossAxisSpacing: 5.0,
mainAxisSpacing: 10.0,
childAspectRatio: 16 / 5,
// children: ...,
);
GridView.builder
上面我们介绍的GridView都需要一个widget数组作为其子元素,这些方式都会提前将所有子widget都构建好,所以只适用于子widget数量比较少时,当子widget比较多时,我们可以通过GridView.builder
来动态创建子widget。GridView.builder
必须指定的参数有两个:
GridView.builder(
...
@required SliverGridDelegate gridDelegate,
@required IndexedWidgetBuilder itemBuilder,
itemCount,
)
其中itemBuilder
为子widget构建器。
示例:
假设我们需要从一个异步数据源(如网络)分批获取一些数据,然后用GridView
来展示:
import "package:flutter/material.dart";
import "package:english_words/english_words.dart";
class TestPage extends StatefulWidget {
@override
_TestPageState createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> {
var _myData = [];
@override
void initState() {
super.initState();
_getData();
}
//模拟异步获取数据
_getData() {
Future.delayed(Duration(seconds: 2)).then((val) {
setState(() {
var generateWords = generateWordPairs().take(10);
var words = generateWords.map((v) => v.asPascalCase).toList();
_myData.addAll(words);
});
});
}
@override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 4,
),
itemCount: _myData.length,
itemBuilder: (context, index) {
return Container(
color: Colors.primaries[index % Colors.primaries.length],
child: Text('单词 ${_myData[index]}'),
);
},
);
}
}
更多
Flutter的GridView
默认子元素显示空间是相等的,但在实际开发中,你可能会遇到子元素大小不等的情况,如下面这样的布局:
Pub上有一个包“flutter_staggered_grid_view” ,它实现了一个交错GridView的布局模型,可以很轻松的实现这种布局。