前言
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的布局模型,可以很輕松的實現這種布局。