數據量很大的時用矩陣方式排列比較清晰,此時用網格列表組件,即爲GridView組件,可實現多行多列的應用場景。 使用GridView建立網格列表有多種方式:html
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({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({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({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({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); } }
構造 GridView 的委託者,GridView.count 就至關於指定 gridDelegate 爲 SliverGridDelegateWithFixedCrossAxisCount,GridView.extent 就至關於指定 gridDelegate 爲 SliverGridDelegateWithMaxCrossAxisExtent,它們至關於對普通構造方法的一種封裝。它的值是一個 SliverGridDelegate 對象,參考 2.1 SliverGridDelegate。數組
構造 GridView 的委託者,它有兩個實現類:app
該委託者一般用於每一行的子組件個數固定的狀況,它能夠指定以下幾個屬性: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
其他屬性 childAspectRatio、crossAxisSpacing、mainAxisSpacing 同 SliverGridDelegateWithFixedCrossAxisCount。debug
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