一,概述
資料量很大的時用矩陣方式排列比較清晰,此時用網格清單元件,即為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