Flutter實戰(zhàn) GridView

2021-03-08 11:29 更新

GridView可以構(gòu)建一個二維網(wǎng)格列表,其默認構(gòu)造函數(shù)定義如下:

  1. GridView({
  2. Axis scrollDirection = Axis.vertical,
  3. bool reverse = false,
  4. ScrollController controller,
  5. bool primary,
  6. ScrollPhysics physics,
  7. bool shrinkWrap = false,
  8. EdgeInsetsGeometry padding,
  9. @required SliverGridDelegate gridDelegate, //控制子widget layout的委托
  10. bool addAutomaticKeepAlives = true,
  11. bool addRepaintBoundaries = true,
  12. double cacheExtent,
  13. List<Widget> children = const <Widget>[],
  14. })

我們可以看到,GridViewListView的大多數(shù)參數(shù)都是相同的,它們的含義也都相同的,如有疑惑讀者可以翻閱 ListView 一節(jié),在此不再贅述。我們唯一需要關(guān)注的是gridDelegate參數(shù),類型是SliverGridDelegate,它的作用是控制GridView子組件如何排列(layout)。

SliverGridDelegate是一個抽象類,定義了GridView Layout相關(guān)接口,子類需要通過實現(xiàn)它們來實現(xiàn)具體的布局算法。Flutter 中提供了兩個SliverGridDelegate的子類SliverGridDelegateWithFixedCrossAxisCountSliverGridDelegateWithMaxCrossAxisExtent,我們可以直接使用,下面我們分別來介紹一下它們。

#SliverGridDelegateWithFixedCrossAxisCount

該子類實現(xiàn)了一個橫軸為固定數(shù)量子元素的 layout 算法,其構(gòu)造函數(shù)為: JavaScriptdart SliverGridDelegateWithFixedCrossAxisCount({ @required double crossAxisCount, double mainAxisSpacing = 0.0, double crossAxisSpacing = 0.0, double childAspectRatio = 1.0, })

  1. - `crossAxisCount`:橫軸子元素的數(shù)量。此屬性值確定后子元素在橫軸的長度就確定了,即ViewPort橫軸長度除以`crossAxisCount`的商。
  2. - `mainAxisSpacing`:主軸方向的間距。
  3. - `crossAxisSpacing`:橫軸方向子元素的間距。
  4. - `childAspectRatio`:子元素在橫軸長度和主軸長度的比例。由于`crossAxisCount`指定后,子元素橫軸長度就確定了,然后通過此參數(shù)值就可以確定子元素在主軸的長度。
  5. 可以發(fā)現(xiàn),子元素的大小是通過`crossAxisCount``childAspectRatio`兩個參數(shù)共同決定的。注意,這里的子元素指的是子組件的最大顯示空間,注意確保子組件的實際大小不要超出子元素的空間。
  6. 下面看一個例子:
  7. ?```JavaScript
  8. GridView(
  9. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  10. crossAxisCount: 3, //橫軸三個子widget
  11. childAspectRatio: 1.0 //寬高比為1時,子widget
  12. ),
  13. children:<Widget>[
  14. Icon(Icons.ac_unit),
  15. Icon(Icons.airport_shuttle),
  16. Icon(Icons.all_inclusive),
  17. Icon(Icons.beach_access),
  18. Icon(Icons.cake),
  19. Icon(Icons.free_breakfast)
  20. ]
  21. );

圖6-9

#GridView.count

GridView.count構(gòu)造函數(shù)內(nèi)部使用了SliverGridDelegateWithFixedCrossAxisCount,我們通過它可以快速的創(chuàng)建橫軸固定數(shù)量子元素的GridView,上面的示例代碼等價于:

  1. GridView.count(
  2. crossAxisCount: 3,
  3. childAspectRatio: 1.0,
  4. children: <Widget>[
  5. Icon(Icons.ac_unit),
  6. Icon(Icons.airport_shuttle),
  7. Icon(Icons.all_inclusive),
  8. Icon(Icons.beach_access),
  9. Icon(Icons.cake),
  10. Icon(Icons.free_breakfast),
  11. ],
  12. );

#SliverGridDelegateWithMaxCrossAxisExtent

該子類實現(xiàn)了一個橫軸子元素為固定最大長度的 layout 算法,其構(gòu)造函數(shù)為:

  1. SliverGridDelegateWithMaxCrossAxisExtent({
  2. double maxCrossAxisExtent,
  3. double mainAxisSpacing = 0.0,
  4. double crossAxisSpacing = 0.0,
  5. double childAspectRatio = 1.0,
  6. })

maxCrossAxisExtent為子元素在橫軸上的最大長度,之所以是“最大”長度,是因為橫軸方向每個子元素的長度仍然是等分的,舉個例子,如果 ViewPort 的橫軸長度是450,那么當(dāng)maxCrossAxisExtent的值在區(qū)間[450/4,450/3)內(nèi)的話,子元素最終實際長度都為112.5,而childAspectRatio所指的子元素橫軸和主軸的長度比為最終的長度比。其它參數(shù)和SliverGridDelegateWithFixedCrossAxisCount相同。

下面我們看一個例子:

  1. GridView(
  2. padding: EdgeInsets.zero,
  3. gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
  4. maxCrossAxisExtent: 120.0,
  5. childAspectRatio: 2.0 //寬高比為2
  6. ),
  7. children: <Widget>[
  8. Icon(Icons.ac_unit),
  9. Icon(Icons.airport_shuttle),
  10. Icon(Icons.all_inclusive),
  11. Icon(Icons.beach_access),
  12. Icon(Icons.cake),
  13. Icon(Icons.free_breakfast),
  14. ],
  15. );

圖6-10

#GridView.extent

GridView.extent 構(gòu)造函數(shù)內(nèi)部使用了 SliverGridDelegateWithMaxCrossAxisExtent,我們通過它可以快速的創(chuàng)建縱軸子元素為固定最大長度的的 GridView,上面的示例代碼等價于:

  1. GridView.extent(
  2. maxCrossAxisExtent: 120.0,
  3. childAspectRatio: 2.0,
  4. children: <Widget>[
  5. Icon(Icons.ac_unit),
  6. Icon(Icons.airport_shuttle),
  7. Icon(Icons.all_inclusive),
  8. Icon(Icons.beach_access),
  9. Icon(Icons.cake),
  10. Icon(Icons.free_breakfast),
  11. ],
  12. );

#GridView.builder

上面我們介紹的 GridView 都需要一個 widget 數(shù)組作為其子元素,這些方式都會提前將所有子 widget 都構(gòu)建好,所以只適用于子 widget 數(shù)量比較少時,當(dāng)子 widget 比較多時,我們可以通過GridView.builder來動態(tài)創(chuàng)建子 widget。GridView.builder 必須指定的參數(shù)有兩個:

  1. GridView.builder(
  2. ...
  3. @required SliverGridDelegate gridDelegate,
  4. @required IndexedWidgetBuilder itemBuilder,
  5. )

其中itemBuilder為子 widget 構(gòu)建器。

#示例

假設(shè)我們需要從一個異步數(shù)據(jù)源(如網(wǎng)絡(luò))分批獲取一些Icon,然后用GridView來展示:

  1. class InfiniteGridView extends StatefulWidget {
  2. @override
  3. _InfiniteGridViewState createState() => new _InfiniteGridViewState();
  4. }
  5. class _InfiniteGridViewState extends State<InfiniteGridView> {
  6. List<IconData> _icons = []; //保存Icon數(shù)據(jù)
  7. @override
  8. void initState() {
  9. // 初始化數(shù)據(jù)
  10. _retrieveIcons();
  11. }
  12. @override
  13. Widget build(BuildContext context) {
  14. return GridView.builder(
  15. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  16. crossAxisCount: 3, //每行三列
  17. childAspectRatio: 1.0 //顯示區(qū)域?qū)捀呦嗟? ),
  18. itemCount: _icons.length,
  19. itemBuilder: (context, index) {
  20. //如果顯示到最后一個并且Icon總數(shù)小于200時繼續(xù)獲取數(shù)據(jù)
  21. if (index == _icons.length - 1 && _icons.length < 200) {
  22. _retrieveIcons();
  23. }
  24. return Icon(_icons[index]);
  25. }
  26. );
  27. }
  28. //模擬異步獲取數(shù)據(jù)
  29. void _retrieveIcons() {
  30. Future.delayed(Duration(milliseconds: 200)).then((e) {
  31. setState(() {
  32. _icons.addAll([
  33. Icons.ac_unit,
  34. Icons.airport_shuttle,
  35. Icons.all_inclusive,
  36. Icons.beach_access, Icons.cake,
  37. Icons.free_breakfast
  38. ]);
  39. });
  40. });
  41. }
  42. }

  • _retrieveIcons():在此方法中我們通過Future.delayed來模擬從異步數(shù)據(jù)源獲取數(shù)據(jù),每次獲取數(shù)據(jù)需要200毫秒,獲取成功后將新數(shù)據(jù)添加到 _icons,然后調(diào)用 setState 重新構(gòu)建。
  • 在 itemBuilder 中,如果顯示到最后一個時,判斷是否需要繼續(xù)獲取數(shù)據(jù),然后返回一個 Icon。

#更多

Flutter 的GridView默認子元素顯示空間是相等的,但在實際開發(fā)中,你可能會遇到子元素大小不等的情況,如下面這樣的布局:

圖6-11

Pub 上有一個包“flutter_staggered_grid_view” ,它實現(xiàn)了一個交錯 GridView 的布局模型,可以很輕松的實現(xiàn)這種布局,詳情讀者可以自行了解。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號