那個,Card常常做圓角背景,然后,Card和ListTile,是經(jīng)常一起玩,ListTile可以試下各種豐富的item效果,因此ListTile經(jīng)常和列表一起玩。
最后,說說九宮格和相對布局。
文中參閱了很多文章,感謝各位大佬。
不說了,開始吧。先來個Card

一、Card和ListTile
安卓里面,有CardView。Flutter為什么有Card,不言而喻了。
一.1、Card
Card的構(gòu)造函數(shù)
* 卡片布局,相當于Android中的CardView
* const Card({
Key key,
this.color,//背景色
this.elevation,//陰影大小
this.shape,//設置邊,可以設置圓角
this.margin = const EdgeInsets.all(4.0),
this.clipBehavior = Clip.none,
this.child,
this.semanticContainer = true,
})
-
key相當于id -
color顏色 -
elevation陰影大小 -
shape設置邊,可以設置圓角 -
margin外邊距 -
clipBehavior對Widget截取的行為,比如 Clip.antiAlias 指抗鋸齒 -
semanticContainer語義容器? 默認為true
例子
一個簡單的例子,演示了邊框,陰影的。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: new CardSimple1()
);
}
}
class CardSimple1 extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Container(
width: 200,
height: 200,
child: Card(
color: Colors.red,
// 普通的邊
shape: Border.all(
color: Colors.yellow,
width: 5.0
),
elevation: 20,// 陰影大小
child: new Text("Card Widget"),
),
)
);
}
}

看呢,是看到了,但是不是我們熟悉的原角CardView。
來個圓角的
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: new CardSimple1()
);
}
}
class CardSimple1 extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Container(
width: 200,
height: 200,
child: Card(
color: Colors.red,
//設置圓角
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
// 普通的邊
/* shape: Border.all(
color: Colors.yellow,
width: 5.0
),*/
elevation: 20,// 陰影大小
child: new Text("Card Widget"),
),
)
);
}
}
顯而易見,我們通過RoundedRectangleBorder實現(xiàn)Card的圓角。
而且,Card里面的元素,居然顯示在Card之外,這目前不知道怎么解決。

加上個抗鋸齒吧 clipBehavior
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: new CardSimple1());
}
}
class CardSimple1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Card(
//設置圓角
// shape:
// RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
color: Colors.purple,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20.0)),),
// 普通的邊
/* shape: Border.all(
color: Colors.yellow,
width: 5.0
),*/
// 抗鋸齒
clipBehavior: Clip.antiAlias,
elevation: 20, // 陰影大小
child: new Container(
width: 200,
height: 200,
alignment: Alignment.center,
child: new Text("Card Widget",style: TextStyle(color: Colors.white),),
)),
);
}
}

指定角,實現(xiàn)圓角
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(50.0),
topRight: Radius.circular(50.0),
bottomLeft: Radius.zero,
bottomRight: Radius.zero),
),

一.2、ListTile
Card和ListTile,是經(jīng)常一起玩。
ListTile的構(gòu)造函數(shù)
const ListTile({
Key key,
this.leading,
this.title,
this.subtitle,
this.trailing,
this.isThreeLine = false,
this.dense,
this.contentPadding,
this.enabled = true,
this.onTap,
this.onLongPress,
this.selected = false,
})
-
key相當于毆打 -
leading將圖像或圖標添加到列表的開頭。這通常是一個圖標。 -
title標題 -
subtitle子標題 -
trailing設置拖尾將在列表的末尾放置一個圖像。這對于指示主-細節(jié)布局特別有用。(trailing本身是拖尾的意思) -
isThreeLine = false默認為false 3行,當列表標題、副標題,有需要更多的空間來容納長度超過一行的文本,可開啟 -
dense讓文本變小 (dense本身是稠密的意思) -
contentPadding內(nèi)容的padding -
enabled = true可否點擊??赏ㄟ^將 enable 設置為 false,來禁止點擊事件 -
onTap點擊 -
onLongPress長按 -
selected = false是否選中,默認為否 ,如果選中列表的 item 項,那么文本和圖標的顏色將成為主題的主顏色。
.
.
.
基本屬性先用起來
這4個,先一起來,leading、title、subtitle、trailing
leading
將圖像或圖標添加到列表的開頭。這通常是一個圖標。
title
標題
subtitle
子標題
trailing
設置拖尾將在列表的末尾放置一個圖像。這對于指示主-細節(jié)布局特別有用。(trailing本身是拖尾的意思)
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: new ListTileSimle());
}
}
class ListTileSimle extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Card(
color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20.0)),),
// 抗鋸齒
clipBehavior: Clip.antiAlias,
elevation: 20,
// 陰影大小
child: new Container(
height: 100,
alignment: Alignment.center,
// 演示 ListTile
child: new ListTile(
title: new Text("海賊王"),
subtitle: new Text("來自東海的路飛"),
// item左側(cè)的圖像
leading: new Icon(
Icons.ac_unit,
color: Colors.blue[500],
),
// 列表尾部的圖標
trailing: new Icon(Icons.chevron_right),
),
)),
);
}
}

其他屬性的使用
-
isThreeLine= false 默認為false 3行,當列表標題、副標題,有需要更多的空間來容納長度超過一行的文本,可開啟 -
dense讓文本變小 (dense本身是稠密的意思) -
contentPadding內(nèi)容的padding -
enabled = true可否點擊??赏ㄟ^將 enable 設置為 false,來禁止點擊事件 -
onTap點擊 -
onLongPress長按 -
selected = false是否選中,默認為否 ,如果選中列表的 item 項,那么文本和圖標的顏色將成為主題的主顏色。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: new ListTileSimle());
}
}
class ListTileSimle extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Card(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)),
),
// 抗鋸齒
clipBehavior: Clip.antiAlias,
elevation: 20,
// 陰影大小
child: new Container(
height: 100,
alignment: Alignment.center,
// 演示 ListTile
child: new ListTile(
title: new Text("海賊王"),
subtitle: new Text("來自東海的路飛"),
// item左側(cè)的圖像
leading: new Icon(
Icons.ac_unit,
color: Colors.blue[500],
),
// 列表尾部的圖標
trailing: new Icon(Icons.chevron_right),
isThreeLine:true,
dense: true, // 讓文本變小
contentPadding:EdgeInsets.symmetric(horizontal: 20.0),
selected: true, // 如果選中列表的 item 項,那么文本和圖標的顏色將成為主題的主顏色。
onTap: () { // 點擊會有水波紋效果
// do something
},
onLongPress: (){
// do something else
},
),
)),
);
}
}

.
.
.
二 、列表 ListView
ListView,做列表呀。
ListView的屬性
ListView({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
this.itemExtent,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
bool addSemanticIndexes = true,
double cacheExtent,
List<Widget> children = const <Widget>[],
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down,
})
常見屬性
-
scrollDirection
Axis 設置滾動的方向,horizontal(水平)或vertical(垂直) - reverse
bool 是否翻轉(zhuǎn) -
itemExtent
double 滾動方向子控件的長度,垂直方向即為高度,水平方向即為寬度 -
controller
ScrollController 用來控制滾動位置及監(jiān)聽滾動事件 -
shrinkWrap
bool 是否根據(jù)子widget的總長度來設置ListView的長度 -
padding
EdgeInsetsGeometry 間距 -
children
List 子控件
primary
bool 當內(nèi)容不足以滾動時,是否支持滾動;對于iOS系統(tǒng)還有一個效果:當用戶點擊狀態(tài)欄時是否滑動到頂部。-
physics
ScrollPhysics:控制用戶滾動視圖的交互- AlwaysScrollableScrollPhysics:列表總是可滾動的。在iOS上會有回彈效果,在android上不會回彈。那么問題來了,如果primary設置為false(內(nèi)容不足時不滾動),且 physics設置為AlwaysScrollableScrollPhysics,列表是否可以滑動?答案是可以,感興趣的可以試一下
- PageScrollPhysics:一般是給PageView控件用的滑動效果。如果listview設置的話在滑動到末尾時會有個比較大的彈起和回彈
- ClampingScrollPhysics:滾動時沒有回彈效果,同android系統(tǒng)的listview效果
- NeverScrollableScrollPhysics:就算內(nèi)容超過列表范圍也不會滑動
- BouncingScrollPhysics:不論什么平臺都會有回彈效果
- FixedExtentScrollPhysics:不適用于ListView,原因:需要指定scroller為 - FixedExtentScrollController,這個scroller只能用于ListWheelScrollViews
shrinkWrap: scroll view在滑動方向上的高度是否由內(nèi)容高度決定,false:則高度為滑動方向上的最大允許高度;如果在滑動方向上沒有設置約束,則這個字段必須設置為true,否則會報錯。cacheExtent:可見區(qū)域的前后會有一定高度的空間去緩存子控件,當滑動時就可以迅速呈現(xiàn)semanticChildCount:有含義的子控件的數(shù)量,如ListView會用children的長度,ListView.separated會用children長度的一半
用于構(gòu)造SliverChildListDelegate的屬性
- addAutomaticKeepAlives:是否將子控件包裹在AutomaticKeepAlive控件內(nèi)
- addRepaintBoundaries:true:是否將子控件包裹在 RepaintBoundary 控件內(nèi)。用于避免列表滾動時的重繪,如果子控件重繪開銷很小時,比如子控件就是個色塊或簡短的文字,把這個字段設置為false性能會更好
- addSemanticIndexes:是否把子控件包裝在IndexedSemantics里,用來提供無障礙語義
來個簡單例子吧
Flutter ListView 用法詳解這個文章,根據(jù)構(gòu)造方法不同,列舉了幾個場景,看起來,比較恰當,本文也是這么來。
方式1 默認構(gòu)造函數(shù)(傳入 List children) 少數(shù)子View
適用場景:已知有限個Item的情況下
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
final title = 'list';
return new MaterialApp(
title: title,
home: new Scaffold(
appBar: new AppBar(
title: new Text(title),
),
body: new Center(
child: new ListViewSimle1(),
),
),
);
}
}
class ListViewSimle1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return new ListView(
//控制方向 默認是垂直的
scrollDirection: Axis.vertical,
children: <Widget>[
TileSimle(),
TileSimle(),
TileSimle(),
],
);
}
}
class TileSimle extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Card(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)),
),
// 抗鋸齒
clipBehavior: Clip.antiAlias,
elevation: 20,
// 陰影大小
child: new Container(
height: 100,
alignment: Alignment.center,
// 演示 ListTile
child: new ListTile(
title: new Text("海賊王"),
subtitle: new Text("來自東海的路飛"),
// item左側(cè)的圖像
leading: new Icon(
Icons.ac_unit,
color: Colors.blue[500],
),
// 列表尾部的圖標
trailing: new Icon(Icons.chevron_right),
),
)),
);
}
}

方式2 默認構(gòu)造函數(shù)(傳入 List children) 少數(shù)子View
適用場景:長列表時采用builder模式,能提高性能。不是把所有子控件都構(gòu)造出來,而是在控件viewport加上頭尾的cacheExtent這個范圍內(nèi)的子Item才會被構(gòu)造。在構(gòu)造時傳遞一個builder,按需加載是一個慣用模式,能提高加載性能。
就是類似安卓的ViewHolder。
簡單例子1
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
final title = 'list';
return new MaterialApp(
title: title,
home: new Scaffold(
appBar: new AppBar(
title: new Text(title),
),
body: new Center(
// child: new ListViewSimle1(),
child: new ListView.builder(
itemBuilder: (context, index) => Text("Item $index"),
itemCount: 100),
),
),
);
}
}

構(gòu)造多種樣式的Item
abstract class ListItem {}
class HeadingItem implements ListItem {
final String heading;
HeadingItem(this.heading);
}
class MessageItem implements ListItem {
final String sender;
final String body;
MessageItem(this.sender, this.body);
}
ListView.builder(
itemBuilder: (context, index) {
final item = items[index];
if (item is HeadingItem) {
return ListTile(
title: Text(
item.heading,
style: Theme.of(context).textTheme.headline,
),
);
} else if (item is MessageItem) {
return ListTile(
title: Text(item.sender),
subtitle: Text(item.body),
);
}
},
itemCount: items.length))
方式3 separated 帶分割線 dart
適用場景:列表中需要分割線時,可以自定義復雜的分割線
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
final title = 'list';
return new MaterialApp(
title: title,
home: new Scaffold(
appBar: new AppBar(
title: new Text(title),
),
body: new Center(
// child: new ListViewSimle1(),
child: ListView.separated(
itemBuilder: (context, index) {
return Text("Item $index");
},
separatorBuilder: (context, index) {
return Container(
color: Colors.grey,
height: 3,
);
},
itemCount: 100)
),
),
);
}
}

方式4 custom 自定義
適用場景:上面幾種模式基本可以滿足業(yè)務需求,如果你還想做一些其它設置(如列表的最大滾動范圍)或獲取滑動時每次布局的子Item范圍,可以嘗試custom模式
- 看看構(gòu)造函數(shù)
const ListView.custom({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
this.itemExtent,
@required this.childrenDelegate,
double cacheExtent,
int semanticChildCount,
})
發(fā)現(xiàn)有一個是必須復寫的 —— childrenDelegate。
.
.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
final title = 'list';
return new MaterialApp(
title: title,
home: new Scaffold(
appBar: new AppBar(
title: new Text(title),
),
body: new Center(
// child: new ListViewSimle1(),
child: ListView.custom(childrenDelegate: CustomSliverChildDelegate())
),
),
);
}
}
class CustomSliverChildDelegate extends SliverChildDelegate {
/// 根據(jù)index構(gòu)造child
@override
Widget build(BuildContext context, int index) {
// KeepAlive將把所有子控件加入到cache,已輸入的TextField文字不會因滾動消失
// 僅用于演示
return KeepAlive(
keepAlive: true,
child: TextField(decoration: InputDecoration(hintText: '請輸入')));
}
/// 決定提供新的childDelegate時是否需要重新build。在調(diào)用此方法前會做類型檢查,不同類型時才會調(diào)用此方法,所以一般返回true。
@override
bool shouldRebuild(SliverChildDelegate oldDelegate) {
return true;
}
/// 提高children的count,當無法精確知道時返回null。
/// 當 build 返回 null時,它也將需要返回一個非null值
@override
int get estimatedChildCount => 100;
/// 預計最大可滑動高度,如果設置的過小會導致部分child不可見,設置報錯
@override
double estimateMaxScrollOffset(int firstIndex, int lastIndex,
double leadingScrollOffset, double trailingScrollOffset) {
return 2500;
}
/// 完成layout后的回調(diào),可以通過該方法獲已完成布局的視圖樹包括哪些子控件
@override
void didFinishLayout(int firstIndex, int lastIndex) {
print('didFinishLayout firstIndex=$firstIndex firstIndex=$lastIndex');
}
}

參考:https://juejin.im/post/5cb1c9d5f265da037371777f
三 、列表 GridView
ListView都說完了,類似九宮格的GridView還會遠嗎
GridView的構(gòu)造函數(shù)
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,
})
常見屬性
-
scrollDirectionAxis 設置滾動的方向,horizontal(水平)或vertical(垂直) -
reversebool 是否翻轉(zhuǎn) -
controller ScrollController 用來控制滾動位置及監(jiān)聽滾動事件 -
shrinkWrapbool 是否根據(jù)子widget的總長度來設置GridView的長度 -
paddingEdgeInsetsGeometry 間距 -
gridDelegateSliverGridDelegate 控制子Widget如何進行布局 -
childrenList 子控件
其實其他屬性,已經(jīng)沒什么特別,需要重點看看的,也就是gridDelegate。
待會說。
GridView有好幾種使用方式。
GridView的幾種使用方式
方式1、GridView.count
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
final title = 'list';
return new MaterialApp(
title: title,
home: new Scaffold(
appBar: new AppBar(
title: new Text(title),
),
body: new Center(
// child: new ListViewSimle1(),
child: GridView.count(
// 方向,這行沒設置頁可以,默認就是垂直的
scrollDirection:Axis.vertical,
//水平子Widget之間間距
crossAxisSpacing: 10.0,
//垂直子Widget之間間距
mainAxisSpacing: 30.0,
//GridView內(nèi)邊距
padding: EdgeInsets.all(10.0),
//一行的Widget數(shù)量
crossAxisCount: 2,
//子Widget寬高比例
childAspectRatio: 2.0, // 比如2.0,就是 寬/高=2
//子Widget列表
children: getWidgetList(),
),
),
),
);
}
}
List<String> getDataList() {
List<String> list = [];
for (int i = 0; i < 100; i++) {
list.add(i.toString());
}
return list;
}
List<Widget> getWidgetList() {
return getDataList().map((item) => getItemContainer(item)).toList();
}
Widget getItemContainer(String item) {
return Container(
alignment: Alignment.center,
child: Text(
item,
style: TextStyle(color: Colors.white, fontSize: 20),
),
color: Colors.blue,
);
}
如果設定了寬高比childAspectRatio,那么手動設定的寬高會失效

SliverGridDelegate的設置,直接在這種模式使用,但是我們結(jié)合build模式說,感覺合適一些。
方式2、GridView.build
這個,可以好好說下。
SliverGridDelegate,可以控制GridView的布局。
其中,有兩個實現(xiàn)類
-
SliverGridDelegateWithMaxCrossAxisExtent(創(chuàng)建一個具有交叉軸最大值的一個網(wǎng)格布局) -
SliverGridDelegateWithFixedCrossAxisCount(設置交叉軸上子控件的個數(shù))
分開說
實現(xiàn)類1 SliverGridDelegateWithMaxCrossAxisExtent
MaxCrossAxis
創(chuàng)建一個具有交叉軸最大值的一個網(wǎng)格布局,元素的行/列數(shù)不是一定的,動態(tài)設定的
- 對于SliverGridDelegateWithMaxCrossAxisExtent而言,水平方向元素個數(shù)不再固定
- 其水平個數(shù)也就是有幾列,由 maxCrossAxisExtent 和屏幕的寬度以及padding和mainAxisSpacing等決定。
構(gòu)造方法
const SliverGridDelegateWithMaxCrossAxisExtent({
@required this.maxCrossAxisExtent, //子控件的最大寬度,實際寬度是根據(jù)交叉軸的值進行平分,也就是說最大寬度并不一定是實際寬度,很有可能子控件的實際寬度要小于設置的最大寬度
this.mainAxisSpacing = 0.0, //主軸之間的間距
this.crossAxisSpacing = 0.0,//交叉軸之間的間距
this.childAspectRatio = 1.0,//子控件的寬高比
}
記住,最大特點是元素的行/列數(shù)不是一定的,動態(tài)設定的
.
.
- 當 maxCrossAxisExtent 為 50
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
List<String> datas = getDataList();
@override
Widget build(BuildContext context) {
final title = 'list';
return new MaterialApp(
title: title,
home: new Scaffold(
appBar: new AppBar(
title: new Text(title),
),
body: new Center(
// child: new ListViewSimle1(),
child: GridView.builder(
// 方向,這行沒設置頁可以,默認就是垂直的
scrollDirection:Axis.vertical,
itemBuilder: (BuildContext context,int index){
return getItemContainer(datas[index]);
},
// MaxCrossAxis 其水平個數(shù)也就是有幾列,由 maxCrossAxisExtent 和屏幕的寬度以及padding和mainAxisSpacing等決定。
gridDelegate:SliverGridDelegateWithMaxCrossAxisExtent(
//單個子Widget的水平最大寬度
maxCrossAxisExtent: 50,
//水平單個子Widget之間間距
mainAxisSpacing: 20.0,
//垂直單個子Widget之間間距
crossAxisSpacing: 10.0
),
),
),
),
);
}
}
List<String> getDataList() {
List<String> list = [];
for (int i = 0; i < 100; i++) {
list.add(i.toString());
}
return list;
}
List<Widget> getWidgetList() {
return getDataList().map((item) => getItemContainer(item)).toList();
}
Widget getItemContainer(String item) {
return Container(
alignment: Alignment.center,
child: Text(
item,
style: TextStyle(color: Colors.white, fontSize: 20),
),
color: Colors.blue,
);
}

.
.
-
當 maxCrossAxisExtent 為 100
image.png
如果強制設置行/列數(shù)就不開心,怎么辦?比如指定為4行,可以試試:
maxCrossAxisExtent: MediaQuery.of(context).size.width/4
通過MediaQuery.of(context).size.width就可得到屏幕的寬度,除以4,就是子控件的最大值,這樣一來我們就可以確定要顯示的子控件的個數(shù)了
其實使用了 MaxCrossAxis 還制定數(shù)量,感覺就是不合適的,有病的。
想指定數(shù)量,那么應該用 SliverGridDelegateWithFixedCrossAxisCount,人家說了FixedCrossAxisCount.
實現(xiàn)類2 SliverGridDelegateWithMaxCrossAxisExtent
核心就是:crossAxisCount 指定數(shù)量
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
List<String> datas = getDataList();
@override
Widget build(BuildContext context) {
final title = 'list';
return new MaterialApp(
title: title,
home: new Scaffold(
appBar: new AppBar(
title: new Text(title),
),
body: new Center(
// child: new ListViewSimle1(),
child: GridView.builder(
// 方向,這行沒設置頁可以,默認就是垂直的
scrollDirection:Axis.vertical,
itemBuilder: (BuildContext context,int index){
return getItemContainer(datas[index]);
},
// FixedCrossAxisCount 指定 行/列的數(shù)量,就垂直方向來說,指定行數(shù)
gridDelegate:SliverGridDelegateWithFixedCrossAxisCount(
//橫軸元素個數(shù): 3
crossAxisCount: 3,
//水平單個子Widget之間間距
mainAxisSpacing: 20.0,
//垂直單個子Widget之間間距
crossAxisSpacing: 10.0
),
),
),
),
);
}
}
List<String> getDataList() {
List<String> list = [];
for (int i = 0; i < 100; i++) {
list.add(i.toString());
}
return list;
}
List<Widget> getWidgetList() {
return getDataList().map((item) => getItemContainer(item)).toList();
}
Widget getItemContainer(String item) {
return Container(
alignment: Alignment.center,
child: Text(
item,
style: TextStyle(color: Colors.white, fontSize: 20),
),
color: Colors.blue,
);
}

.
.
方式3、GridView.custom
這個也沒什么特別的,之前ListView也有說過了,類似
- 構(gòu)造函數(shù)
const GridView.custom({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
@required this.gridDelegate,
@required this.childrenDelegate,
double cacheExtent,
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down,
})
看看構(gòu)造函數(shù),有兩個必須復寫的,簡單示例下
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
List<String> datas = getDataList();
@override
Widget build(BuildContext context) {
final title = 'list';
return new MaterialApp(
title: title,
home: new Scaffold(
appBar: new AppBar(
title: new Text(title),
),
body: new Center(
// child: new ListViewSimle1(),
child: GridView.custom(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, mainAxisSpacing: 10.0, crossAxisSpacing: 20.0, ),
childrenDelegate: SliverChildBuilderDelegate((context, position) {
return getItemContainer(datas[position]);
}, childCount: datas.length))
),
),
);
}
}
List<String> getDataList() {
List<String> list = [];
for (int i = 0; i < 100; i++) {
list.add(i.toString());
}
return list;
}
List<Widget> getWidgetList() {
return getDataList().map((item) => getItemContainer(item)).toList();
}
Widget getItemContainer(String item) {
return Container(
alignment: Alignment.center,
child: Text(
item,
style: TextStyle(color: Colors.white, fontSize: 20),
),
color: Colors.blue,
);
}

參考:
https://blog.csdn.net/yuzhiqiang_1993/article/details/87968234
Flutter GridView
四、相對布局Stack
這貨。堆疊的意思,跟安卓比,類似于相對布局,誰出現(xiàn)的晚,誰就可以出現(xiàn)在上方,覆蓋別人。
看看構(gòu)造函數(shù)
Stack({
Key key,
this.alignment = AlignmentDirectional.topStart,
this.textDirection,
this.fit = StackFit.loose,
this.overflow = Overflow.clip,
List<Widget> children = const <Widget>[],
})
-
alignment:對齊方式,默認是左上角(topStart)。 -
textDirection:文本的方向,絕大部分不需要處理。 -
fit:定義如何設置non-positioned節(jié)點尺寸,默認為loose。
其中StackFit有如下幾種:-
loose:子節(jié)點寬松的取值,可以從min到max的尺寸; -
expand:子節(jié)點盡可能的占用空間,取max尺寸; -
passthrough:不改變子節(jié)點的約束條件。
-
-
overflow:超過的部分是否裁剪掉(clipped)。
布局行為
Stack的布局行為,根據(jù)child是positioned還是non-positioned來區(qū)分。
- 對于positioned的子節(jié)點,它們的位置會根據(jù)所設置的top、bottom、right以及l(fā)eft屬性來確定,這幾個值都是相對于Stack的左上角;
- 對于non-positioned的子節(jié)點,它們會根據(jù)Stack的aligment(左上角)來設置位置。
對于繪制child的順序,則是第一個child被繪制在最底端,后面的依次在前一個child的上面。調(diào)整先后出現(xiàn),可以改變展示順序。
例子
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
List<String> datas = getDataList();
@override
Widget build(BuildContext context) {
final title = 'list';
return new MaterialApp(
title: title,
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
//關(guān)鍵代碼
var stack = new Stack(
alignment: const Alignment(0.0, 0.6), //分析 2
children: [
new CircleAvatar( //分析 3
backgroundImage: new AssetImage('images/lake.jpg'),
radius: 100.0,
),
new Container( //分析 4
decoration: new BoxDecoration(
color: Colors.black45,
),
child: new Text(
'添加水印',
style: new TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
],
);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
elevation: 5.0,
),
body: Center( //分析 1
child: stack,
),
);
}
}

關(guān)于Stack,還有一個IndexedStack,這里就不展開了。
參考:
Flutter 布局(八)- Stack、IndexedStack、GridView詳解
http://m.itdecent.cn/p/f1b8fbe5cda0
.
.
.
END
