天天看點

【Flutter學習】基本元件之基本清單Gradview元件

一,概述  

資料量很大的時用矩陣方式排列比較清晰,此時用網格清單元件,即為GridView元件,可實作多行多列的應用場景。 使用GridView建立網格清單有多種方式:

  • 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 的委托者,它有兩個實作類:

  • SliverGridDelegateWithFixedCrossAxisCount

      該委托者通常用于每一行的子元件個數固定的情況,它可以指定如下幾個屬性:

    • crossAxisCount:必傳參數,Cross 軸(在 GridView 中通常是橫軸,即每一行)子元件個數。
    • childAspectRatio:子元件寬高比,如 2 表示寬:高=2:1,如 0.5 表示寬:高=0.5:1=1:2,簡單來說就是值大于 1 就會寬大于高,小于 1 就會寬小于高。
    • crossAxisSpacing:Cross 軸子元件的間隔,一行中第一個子元件左邊不會添加間隔,最後一個子元件右邊不會添加間隔,這一點很棒。
    • mainAxisSpacing:Main 軸(在 GridView 中通常是縱軸,即每一列)子元件間隔,也就是每一行之間的間隔,同樣第一行的上邊和最後一行的下邊不會添加間隔。
  • SliverGridDelegateWithMaxCrossAxisExtent
    • maxCrossAxisExtent:必傳參數,
    • Cross 軸(在 GridView 中通常是橫軸,即每一行)子元件最大寬度,會根據該值來決定一行擺放幾個子元件。

    其餘屬性 childAspectRatio、crossAxisSpacing、mainAxisSpacing 同 SliverGridDelegateWithFixedCrossAxisCount。

五,示例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);
  }
}      

五,官方文檔

  官方文檔

轉載于:https://www.cnblogs.com/lxlx1798/p/11063857.html