【Flutter】可滾動組件之GridView


前言

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM