【Flutter學習】基本組件之基本網格Gradview組件

一,概述  

數據量很大的時用矩陣方式排列比較清晰,此時用網格列表組件,即爲GridView組件,可實現多行多列的應用場景。 使用GridView建立網格列表有多種方式:html

  • GridView.count 經過單行展現個數建立GridView。
  • GridView.extend經過最大寬度建立GridView。

二,構造函數  

  • GridView

    • 使用場景:使用自定義SliverGridDelegate建立可滾動的2D小部件數組
    • 構造函數
      GridView({Key key, 
      Axis scrollDirection: Axis.vertical,
      bool reverse: false, ScrollController controller, bool primary, ScrollPhysics physics, bool shrinkWrap: false, EdgeInsetsGeometry padding, @required SliverGridDelegate gridDelegate, bool addAutomaticKeepAlives: true, bool addRepaintBoundaries: true, bool addSemanticIndexes: true, double cacheExtent, List<Widget> children: const [], int semanticChildCount
      })
  • GridView.count

    • 使用場景:建立一個可滾動的2D小部件數組,在橫軸上具備固定數量的網格塊
    • 構造函數
      GridView.count({Key key, Axis scrollDirection: Axis.vertical, 
        bool reverse: false, ScrollController controller, 
        bool primary, ScrollPhysics physics, bool shrinkWrap: false, 
        EdgeInsetsGeometry padding, @required int crossAxisCount,
        double mainAxisSpacing: 0.0, double crossAxisSpacing: 0.0, 
         double childAspectRatio: 1.0, bool addAutomaticKeepAlives: true,
         bool addRepaintBoundaries: true, 
         bool addSemanticIndexes: true,
         double cacheExtent, List<Widget> children: const [], 
         int semanticChildCount 
      })
    • 分析和使用
      Widget gridViewDefaultCount(List<BaseBean> list) {
          return GridView.count(
      //      padding: EdgeInsets.all(5.0),
            //一行多少個
            crossAxisCount: 5,
            //滾動方向
            scrollDirection: Axis.vertical,
            // 左右間隔
            crossAxisSpacing: 10.0,
            // 上下間隔
            mainAxisSpacing: 10.0,
            //寬高比
            childAspectRatio: 2 / 5,
      
            children: initListWidget(list),
          );
        }
      
      List<Widget> initListWidget(List<BaseBean> list) {
          List<Widget> lists = [];
          for (var item in list) {
            lists.add(new Container(
              height: 50.0,
              width: 50.0,
              color: Colors.yellow,
              child: new Center(
                  child: new Text(
                item.age.toString(),
              )),
            ));
          }
          return lists;
        }
  • GridView.extent

    • 使用場景:使用每一個都具備最大橫軸範圍的 網格塊 建立可滾動的2D小部件數組。
    • 構造函數
      GridView.extent({Key key, Axis scrollDirection: Axis.vertical,
         bool reverse: false, ScrollController controller,
         bool primary, ScrollPhysics physics, 
         bool shrinkWrap: false, EdgeInsetsGeometry padding,
         @required double maxCrossAxisExtent,
         double mainAxisSpacing: 0.0, double crossAxisSpacing: 0.0,
         double childAspectRatio: 1.0, 
         bool addAutomaticKeepAlives: true,
         bool addRepaintBoundaries: true, 
         bool addSemanticIndexes: true,
         List<Widget> children: const [], 
         int semanticChildCount 
      })
    • 分析和使用  
      ///GridView.extent 容許您指定項的最大像素寬度
        Widget gridViewDefaultExtent(List<BaseBean> list) {
          return GridView.extent(
            ///設置item的最大像素寬度  好比 130
            maxCrossAxisExtent: 130.0,
            ///其餘屬性和count同樣
            children: initListWidget(list),
          );
        }
  • GridView.builder

    • 使用場景:建立按需建立的可滾動的2D小部件數組
    • 構造函數
      GridView.builder({Key key, Axis scrollDirection: Axis.vertical,
         bool reverse: false, ScrollController controller, 
          bool primary, ScrollPhysics physics,
          bool shrinkWrap: false, EdgeInsetsGeometry padding, 
          @required SliverGridDelegate gridDelegate, 
          @required IndexedWidgetBuilder itemBuilder,
          int itemCount, bool addAutomaticKeepAlives: true,
          bool addRepaintBoundaries: true, 
          bool addSemanticIndexes: true, 
          double cacheExtent, int semanticChildCount 
      })
    • 分析和使用
      ///GridView.builder  能夠定義gridDelegate的模式
        Widget gridViewDefaultBuilder(List<BaseBean> list) {
          return GridView.builder(
              gridDelegate: MyGridViewDefaultCustom(
                crossAxisCount: 2,
                mainAxisSpacing: 10.0,
                crossAxisSpacing: 10.0,
                childAspectRatio: 1.0,
              ),
              itemBuilder: (context, i) => new Container(
                    child: new Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: <Widget>[
                        new Text(
                          "${list[i].name}",
                          style: new TextStyle(fontSize: 18.0, color: Colors.red),
                        ),
                        new Text(
                          "${list[i].age}",
                          style: new TextStyle(fontSize: 18.0, color: Colors.green),
                        ),
                        new Text(
                          "${list[i].content}",
                          style: new TextStyle(fontSize: 18.0, color: Colors.blue),
                        ),
                      ],
                    ),
               ));
        }
      ///自定義SliverGridDelegate
      class MyGridViewDefaultCustom extends SliverGridDelegate {
        ///橫軸上的子節點數。  一行多少個child
        final int crossAxisCount;
      
        ///沿主軸的每一個子節點之間的邏輯像素數。 默認垂直方向的子child間距  這裏的是主軸方向 當你改變 scrollDirection: Axis.vertical,就是改變了主軸發方向
        final double mainAxisSpacing;
      
        ///沿橫軸的每一個子節點之間的邏輯像素數。默認水平方向的子child間距
        final double crossAxisSpacing;
      
        ///每一個孩子的橫軸與主軸範圍的比率。 child的寬高比  經常使用來處理child的適配
        final double childAspectRatio;
      
        bool _debugAssertIsValid() {
          assert(mainAxisSpacing >= 0.0);
          assert(crossAxisSpacing >= 0.0);
          assert(childAspectRatio > 0.0);
          return true;
        }
      
        const MyGridViewDefaultCustom({
          @required this.crossAxisCount,
          this.mainAxisSpacing = 0.0,
          this.crossAxisSpacing = 0.0,
          this.childAspectRatio = 1.0,
        })  : assert(crossAxisCount != null && crossAxisCount > 0),
              assert(mainAxisSpacing != null && mainAxisSpacing >= 0),
              assert(crossAxisSpacing != null && crossAxisSpacing >= 0),
              assert(childAspectRatio != null && childAspectRatio > 0);
      
        ///  返回值有關網格中圖塊大小和位置的信息。這裏就是處理怎麼擺放 咱們能夠本身定義
        ///   SliverGridLayout是抽象類  SliverGridRegularTileLayout繼承於SliverGridLayout是抽象類
        @override
        SliverGridLayout getLayout(SliverConstraints constraints) {
          // TODO: implement getLayout
          assert(_debugAssertIsValid());
      
          ///對參數的修飾 自定義
          final double usableCrossAxisExtent =
              constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1);
          final double childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
          final double childMainAxisExtent = childCrossAxisExtent / childAspectRatio;
          return MySliverGridLayout(
            crossAxisCount: crossAxisCount,
            mainAxisStride: childMainAxisExtent + mainAxisSpacing,
            crossAxisStride: childCrossAxisExtent + crossAxisSpacing,
            childMainAxisExtent: childMainAxisExtent,
            childCrossAxisExtent: childCrossAxisExtent,
            reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
          );
        }
      
        /// 和ListView的 shouldRebuild 做用同樣   以前的實例和新進來的實例是相同的就返回true
        @override
        bool shouldRelayout(SliverGridDelegate oldDelegate) {
          // TODO: implement shouldRelayout
          return true;
        }
      }
  • GridView.custom 

    • 使用場景:使用自定義SliverGridDelegate和自定義SliverChildDelegate建立可滾動的2D小部件數組
    • 構造函數
      GridView.custom({Key key, Axis scrollDirection: Axis.vertical,
        bool reverse: false, ScrollController controller, 
        bool primary, ScrollPhysics physics, 
        bool shrinkWrap: false, 
        EdgeInsetsGeometry padding,
        @required SliverGridDelegate gridDelegate,
        @required SliverChildDelegate childrenDelegate, 
        double cacheExtent,
        int semanticChildCount })
    • 分析和使用  
       ///GridView.custom 就是本身定製規則
        /// 這裏說一下 GridView.count gridDelegate 其實就是內部實現 SliverGridDelegateWithFixedCrossAxisCount
        /// GridView.extent gridDelegate 其實就是內部實現 SliverGridDelegateWithMaxCrossAxisExtent
        Widget gridViewDefaultCustom(List<BaseBean> list) {
          return GridView.custom(
            gridDelegate: MyGridViewDefaultCustom(
              crossAxisCount: 2,
              mainAxisSpacing: 10.0,
              crossAxisSpacing: 10.0,
              childAspectRatio: 1.0,
            ),
            childrenDelegate: MyGridChildrenDelegate(
              (BuildContext context, int i) {
                return new Container(
                    child: new Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                    new Text(
                      "${list[i].name}",
                      style: new TextStyle(fontSize: 18.0, color: Colors.red),
                    ),
                    new Text(
                      "${list[i].age}",
                      style: new TextStyle(fontSize: 18.0, color: Colors.green),
                    ),
                    new Text(
                      "${list[i].content}",
                      style: new TextStyle(fontSize: 18.0, color: Colors.blue),
                    ),
                  ],
                ));
              },
              childCount: list.length,
            ),
          );
        }

       

      /**
       * 繼承SliverChildBuilderDelegate  能夠對列表的監聽
       */
      class MyGridChildrenDelegate extends SliverChildBuilderDelegate {
        MyGridChildrenDelegate(
          Widget Function(BuildContext, int) builder, {
          int childCount,
          bool addAutomaticKeepAlive = true,
          bool addRepaintBoundaries = true,
        }) : super(builder,
                  childCount: childCount,
                  addAutomaticKeepAlives: addAutomaticKeepAlive,
                  addRepaintBoundaries: addRepaintBoundaries);
      
        ///監聽 在可見的列表中 顯示的第一個位置和最後一個位置
        @override
        void didFinishLayout(int firstIndex, int lastIndex) {
          print('firstIndex: $firstIndex, lastIndex: $lastIndex');
        }
      
        ///可不重寫 重寫不能爲null  默認是true  添加進來的實例與以前的實例是否相同 相同返回true 反之false
        ///listView 暫時沒有看到應用場景 源碼中使用在 SliverFillViewport 中
        @override
        bool shouldRebuild(SliverChildBuilderDelegate oldDelegate) {
          // TODO: implement shouldRebuild
          print("oldDelegate$oldDelegate");
          return super.shouldRebuild(oldDelegate);
        }
      }

三,參數詳解

  • gridDelegate:

   構造 GridView 的委託者,GridView.count 就至關於指定 gridDelegate 爲 SliverGridDelegateWithFixedCrossAxisCount,GridView.extent 就至關於指定 gridDelegate 爲 SliverGridDelegateWithMaxCrossAxisExtent,它們至關於對普通構造方法的一種封裝。它的值是一個 SliverGridDelegate 對象,參考 2.1 SliverGridDelegate。數組

  • cacheExtent:同 ListView,預加載的區域。
  • controller:同 ListView,滑動監聽,值爲一個 ScrollController 對象,這個屬性應該能夠用來作下拉刷新和上垃加載,後面詳細研究。
  • padding:同 ListView,整個 GridView 的內間距。
  • physics:同 ListView,設置 GridView 如何響應用戶的滑動行爲,值爲一個 ScrollPhysics 對象,它的實現類經常使用的有:
    • AlwaysScrollableScrollPhysics:老是能夠滑動。
    • NeverScrollableScrollPhysics:禁止滾動。
    • BouncingScrollPhysics:內容超過一屏,上拉有回彈效果。
    • ClampingScrollPhysics:包裹內容,不會有回彈,感受跟 AlwaysScrollableScrollPhysics 差很少。
  • reverse:Item 的順序是否反轉,若爲 true 則反轉,這個翻轉只是行翻轉,即第一行變成最後一行,可是每一行中的子組件仍是從左往右擺放的,用到該屬性的開發情景較少。
  • scrollDirection:GirdView 的方向,爲 Axis.vertical 表示縱向,爲 Axis.horizontal 表示橫向,橫向的話 CrossAxis 和 MainAxis 表示的軸也會調換,爲 Axis.Horizontal 的狀況也較少。
  • semanticChildCount:不太清楚。
  • shrinkWrap:不太清楚。
  • children:子組件,不用多說。

四,關於SliverGridDelegate

構造 GridView 的委託者,它有兩個實現類:app

  • SliverGridDelegateWithFixedCrossAxisCount

      該委託者一般用於每一行的子組件個數固定的狀況,它能夠指定以下幾個屬性:ide

     

    • crossAxisCount:必傳參數,Cross 軸(在 GridView 中一般是橫軸,即每一行)子組件個數。函數

       

    • childAspectRatio:子組件寬高比,如 2 表示寬:高=2:1,如 0.5 表示寬:高=0.5:1=1:2,簡單來講就是值大於 1 就會寬大於高,小於 1 就會寬小於高。ui

       

    • crossAxisSpacing:Cross 軸子組件的間隔,一行中第一個子組件左邊不會添加間隔,最後一個子組件右邊不會添加間隔,這一點很棒。this

       

    • mainAxisSpacing:Main 軸(在 GridView 中一般是縱軸,即每一列)子組件間隔,也就是每一行之間的間隔,一樣第一行的上邊和最後一行的下邊不會添加間隔。spa

  • SliverGridDelegateWithMaxCrossAxisExtent
    • maxCrossAxisExtent:必傳參數,
    • Cross 軸(在 GridView 中一般是橫軸,即每一行)子組件最大寬度,會根據該值來決定一行擺放幾個子組件。

    其他屬性 childAspectRatio、crossAxisSpacing、mainAxisSpacing 同 SliverGridDelegateWithFixedCrossAxisCount。debug

五,示例demo  

import 'package:flutter/material.dart';
import 'package:flutter/src/rendering/sliver.dart';
import 'package:flutter/src/rendering/sliver_grid.dart';
import 'package:flutter_vscode/listview_demo.dart';

class GridViewDemo extends StatefulWidget {
  @override
  _GridViewDemoState createState() => new _GridViewDemoState();
}

class _GridViewDemoState extends State<GridViewDemo> {
  List<BaseBean> gridList;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    gridList = new List<BaseBean>.generate(
        32, (i) => new BaseBean("name$i", i, "content=$i"));
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: "",
      home: new Scaffold(
        appBar: new AppBar(
          centerTitle: true,
          title: new Text("GridView"),
        ),
        body: gridViewDefaultCount(gridList),
      ),
    );
  }

  List<Widget> initListWidget(List<BaseBean> list) {
    List<Widget> lists = [];
    for (var item in list) {
      lists.add(new Container(
        height: 50.0,
        width: 50.0,
        color: Colors.yellow,
        child: new Center(
            child: new Text(
          item.age.toString(),
        )),
      ));
    }
    return lists;
  }

  Widget gridViewDefaultCount(List<BaseBean> list) {
    return GridView.count(
//      padding: EdgeInsets.all(5.0),
      crossAxisCount: 5,
      //一行多少個
      scrollDirection: Axis.vertical,
      //滾動方向
      crossAxisSpacing: 10.0,
      // 左右間隔
      mainAxisSpacing: 10.0,
      // 上下間隔
      childAspectRatio: 2 / 5,
      //寬高比
      children: initListWidget(list),
    );
  }

  ///GridView.extent 容許您指定項的最大像素寬度
  Widget gridViewDefaultExtent(List<BaseBean> list) {
    return GridView.extent(
      ///設置item的最大像素寬度  好比 130
      maxCrossAxisExtent: 130.0,

      ///其餘屬性和count同樣
      children: initListWidget(list),
    );
  }

  ///GridView.custom 就是本身定製規則
  /// 這裏說一下 GridView.count gridDelegate 其實就是內部實現 SliverGridDelegateWithFixedCrossAxisCount
  /// GridView.extent gridDelegate 其實就是內部實現 SliverGridDelegateWithMaxCrossAxisExtent
  Widget gridViewDefaultCustom(List<BaseBean> list) {
    return GridView.custom(
      gridDelegate: MyGridViewDefaultCustom(
        crossAxisCount: 2,
        mainAxisSpacing: 10.0,
        crossAxisSpacing: 10.0,
        childAspectRatio: 1.0,
      ),
      childrenDelegate: MyGridChildrenDelegate(
        (BuildContext context, int i) {
          return new Container(
              child: new Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              new Text(
                "${list[i].name}",
                style: new TextStyle(fontSize: 18.0, color: Colors.red),
              ),
              new Text(
                "${list[i].age}",
                style: new TextStyle(fontSize: 18.0, color: Colors.green),
              ),
              new Text(
                "${list[i].content}",
                style: new TextStyle(fontSize: 18.0, color: Colors.blue),
              ),
            ],
          ));
        },
        childCount: list.length,
      ),
    );
  }

  ///GridView.builder  能夠定義gridDelegate的模式
  Widget gridViewDefaultBuilder(List<BaseBean> list) {
    return GridView.builder(
        gridDelegate: MyGridViewDefaultCustom(
          crossAxisCount: 2,
          mainAxisSpacing: 10.0,
          crossAxisSpacing: 10.0,
          childAspectRatio: 1.0,
        ),
        itemBuilder: (context, i) => new Container(
              child: new Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: <Widget>[
                  new Text(
                    "${list[i].name}",
                    style: new TextStyle(fontSize: 18.0, color: Colors.red),
                  ),
                  new Text(
                    "${list[i].age}",
                    style: new TextStyle(fontSize: 18.0, color: Colors.green),
                  ),
                  new Text(
                    "${list[i].content}",
                    style: new TextStyle(fontSize: 18.0, color: Colors.blue),
                  ),
                ],
              ),
            ));
  }
}

///自定義SliverGridDelegate
class MyGridViewDefaultCustom extends SliverGridDelegate {
  ///橫軸上的子節點數。  一行多少個child
  final int crossAxisCount;

  ///沿主軸的每一個子節點之間的邏輯像素數。 默認垂直方向的子child間距  這裏的是主軸方向 當你改變 scrollDirection: Axis.vertical,就是改變了主軸發方向
  final double mainAxisSpacing;

  ///沿橫軸的每一個子節點之間的邏輯像素數。默認水平方向的子child間距
  final double crossAxisSpacing;

  ///每一個孩子的橫軸與主軸範圍的比率。 child的寬高比  經常使用來處理child的適配
  final double childAspectRatio;

  bool _debugAssertIsValid() {
    assert(mainAxisSpacing >= 0.0);
    assert(crossAxisSpacing >= 0.0);
    assert(childAspectRatio > 0.0);
    return true;
  }

  const MyGridViewDefaultCustom({
    @required this.crossAxisCount,
    this.mainAxisSpacing = 0.0,
    this.crossAxisSpacing = 0.0,
    this.childAspectRatio = 1.0,
  })  : assert(crossAxisCount != null && crossAxisCount > 0),
        assert(mainAxisSpacing != null && mainAxisSpacing >= 0),
        assert(crossAxisSpacing != null && crossAxisSpacing >= 0),
        assert(childAspectRatio != null && childAspectRatio > 0);

  ///  返回值有關網格中圖塊大小和位置的信息。這裏就是處理怎麼擺放 咱們能夠本身定義
  ///   SliverGridLayout是抽象類  SliverGridRegularTileLayout繼承於SliverGridLayout是抽象類
  @override
  SliverGridLayout getLayout(SliverConstraints constraints) {
    // TODO: implement getLayout
    assert(_debugAssertIsValid());

    ///對參數的修飾 自定義
    final double usableCrossAxisExtent =
        constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1);
    final double childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
    final double childMainAxisExtent = childCrossAxisExtent / childAspectRatio;
    return MySliverGridLayout(
      crossAxisCount: crossAxisCount,
      mainAxisStride: childMainAxisExtent + mainAxisSpacing,
      crossAxisStride: childCrossAxisExtent + crossAxisSpacing,
      childMainAxisExtent: childMainAxisExtent,
      childCrossAxisExtent: childCrossAxisExtent,
      reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
    );
  }

  /// 和ListView的 shouldRebuild 做用同樣   以前的實例和新進來的實例是相同的就返回true
  @override
  bool shouldRelayout(SliverGridDelegate oldDelegate) {
    // TODO: implement shouldRelayout
    return true;
  }
}

///自定義SliverGridLayout
class MySliverGridLayout extends SliverGridLayout {
  final int crossAxisCount;

  final double mainAxisStride;

  final double crossAxisStride;

  final double childMainAxisExtent;

  final double childCrossAxisExtent;

  final bool reverseCrossAxis;

  const MySliverGridLayout({
    @required this.crossAxisCount,
    @required this.mainAxisStride,
    @required this.crossAxisStride,
    @required this.childMainAxisExtent,
    @required this.childCrossAxisExtent,
    @required this.reverseCrossAxis,
  })  : assert(crossAxisCount != null && crossAxisCount > 0),
        assert(mainAxisStride != null && mainAxisStride >= 0),
        assert(crossAxisStride != null && crossAxisStride >= 0),
        assert(childMainAxisExtent != null && childMainAxisExtent >= 0),
        assert(childCrossAxisExtent != null && childCrossAxisExtent >= 0),
        assert(reverseCrossAxis != null);

  ///若是有,則徹底顯示全部圖塊所需的滾動範圍
  ///「childCount」兒童總數。
  ///
  ///子計數永遠不會爲空。
  @override
  double computeMaxScrollOffset(int childCount) {
    // TODO: implement computeMaxScrollOffset
    return null;
  }

  ///具備給定索引的子項的大小和位置。
  @override
  SliverGridGeometry getGeometryForChildIndex(int index) {
    // TODO: implement getGeometryForChildIndex
    return null;
  }

  ///在此滾動偏移處(或以前)可見的最大子索引。
  @override
  int getMaxChildIndexForScrollOffset(double scrollOffset) {
    // TODO: implement getMaxChildIndexForScrollOffset
    return null;
  }

  ///在此滾動偏移處(或以後)可見的最小子索引。
  @override
  int getMinChildIndexForScrollOffset(double scrollOffset) {
    // TODO: implement getMinChildIndexForScrollOffset
    return null;
  }
}

// ignore: slash_for_doc_comments
/**
 * 繼承SliverChildBuilderDelegate  能夠對列表的監聽
 */
class MyGridChildrenDelegate extends SliverChildBuilderDelegate {
  MyGridChildrenDelegate(
    Widget Function(BuildContext, int) builder, {
    int childCount,
    bool addAutomaticKeepAlive = true,
    bool addRepaintBoundaries = true,
  }) : super(builder,
            childCount: childCount,
            addAutomaticKeepAlives: addAutomaticKeepAlive,
            addRepaintBoundaries: addRepaintBoundaries);

  ///監聽 在可見的列表中 顯示的第一個位置和最後一個位置
  @override
  void didFinishLayout(int firstIndex, int lastIndex) {
    print('firstIndex: $firstIndex, lastIndex: $lastIndex');
  }

  ///可不重寫 重寫不能爲null  默認是true  添加進來的實例與以前的實例是否相同 相同返回true 反之false
  ///listView 暫時沒有看到應用場景 源碼中使用在 SliverFillViewport 中
  @override
  bool shouldRebuild(SliverChildBuilderDelegate oldDelegate) {
    // TODO: implement shouldRebuild
    print("oldDelegate$oldDelegate");
    return super.shouldRebuild(oldDelegate);
  }
}

五,官方文檔

  官方文檔

code

相關文章
相關標籤/搜索