Flutter實(shí)戰(zhàn) GridView

2021-03-08 11:29 更新

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

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>[],
})

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

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

#SliverGridDelegateWithFixedCrossAxisCount

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

- `crossAxisCount`:橫軸子元素的數(shù)量。此屬性值確定后子元素在橫軸的長(zhǎng)度就確定了,即ViewPort橫軸長(zhǎng)度除以`crossAxisCount`的商。
- `mainAxisSpacing`:主軸方向的間距。
- `crossAxisSpacing`:橫軸方向子元素的間距。
- `childAspectRatio`:子元素在橫軸長(zhǎng)度和主軸長(zhǎng)度的比例。由于`crossAxisCount`指定后,子元素橫軸長(zhǎng)度就確定了,然后通過此參數(shù)值就可以確定子元素在主軸的長(zhǎng)度。


可以發(fā)現(xiàn),子元素的大小是通過`crossAxisCount`和`childAspectRatio`兩個(gè)參數(shù)共同決定的。注意,這里的子元素指的是子組件的最大顯示空間,注意確保子組件的實(shí)際大小不要超出子元素的空間。


下面看一個(gè)例子:


?```JavaScript
GridView(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: 3, //橫軸三個(gè)子widget
      childAspectRatio: 1.0 //寬高比為1時(shí),子widget
  ),
  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)
  ]
);

圖6-9

#GridView.count

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

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

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

SliverGridDelegateWithMaxCrossAxisExtent({
  double maxCrossAxisExtent,
  double mainAxisSpacing = 0.0,
  double crossAxisSpacing = 0.0,
  double childAspectRatio = 1.0,
})

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

下面我們看一個(gè)例子:

GridView(
  padding: EdgeInsets.zero,
  gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
      maxCrossAxisExtent: 120.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),
  ],
);

圖6-10

#GridView.extent

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

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

GridView.builder(
 ...
 @required SliverGridDelegate gridDelegate, 
 @required IndexedWidgetBuilder itemBuilder,
)

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

#示例

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

class InfiniteGridView extends StatefulWidget {
  @override
  _InfiniteGridViewState createState() => new _InfiniteGridViewState();
}


class _InfiniteGridViewState extends State<InfiniteGridView> {


  List<IconData> _icons = []; //保存Icon數(shù)據(jù)


  @override
  void initState() {
    // 初始化數(shù)據(jù)  
    _retrieveIcons();
  }


  @override
  Widget build(BuildContext context) {
    return GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 3, //每行三列
            childAspectRatio: 1.0 //顯示區(qū)域?qū)捀呦嗟?        ),
        itemCount: _icons.length,
        itemBuilder: (context, index) {
          //如果顯示到最后一個(gè)并且Icon總數(shù)小于200時(shí)繼續(xù)獲取數(shù)據(jù)
          if (index == _icons.length - 1 && _icons.length < 200) {
            _retrieveIcons();
          }
          return Icon(_icons[index]);
        }
    );
  }


  //模擬異步獲取數(shù)據(jù)
  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來模擬從異步數(shù)據(jù)源獲取數(shù)據(jù),每次獲取數(shù)據(jù)需要200毫秒,獲取成功后將新數(shù)據(jù)添加到 _icons,然后調(diào)用 setState 重新構(gòu)建。
  • 在 itemBuilder 中,如果顯示到最后一個(gè)時(shí),判斷是否需要繼續(xù)獲取數(shù)據(jù),然后返回一個(gè) Icon。

#更多

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

圖6-11

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

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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)