前言
接Flutter基礎控件篇[2]饥侵,繼續(xù)往下羅列。
這一篇主要記錄Table衣屏、ListView躏升、GridView、SingleChildScrollView狼忱、SimpleDialog膨疏、AlertDialog盗温、自定義彈窗還有一個BottomSheet。
正文
Table
類似于Android的TableLayout成肘,桌面布局卖局,就跟手機桌面似的,幾行幾列双霍。
基本使用:
Widget build(BuildContext context) {
return Table(
children: [
TableRow(children: [
Container(color: Colors.blue[200], margin: EdgeInsets.fromLTRB(5, 0, 5, 0), width: 70, height: 100,),
Container(color: Colors.blue[200], margin: EdgeInsets.fromLTRB(5, 0, 5, 0), width: 70, height: 70,),
Container(color: Colors.blue[200], margin: EdgeInsets.fromLTRB(5, 0, 5, 0), width: 70, height: 70,),
]),
TableRow(children: [
Container(color: Colors.blue[300], margin: EdgeInsets.fromLTRB(5, 0, 5, 0), width: 70, height: 70,),
Container(color: Colors.blue[300], margin: EdgeInsets.fromLTRB(5, 0, 5, 0), width: 70, height: 100, alignment: Alignment.center, child: Text('Table, 3行3列'),),
Container(color: Colors.blue[300], margin: EdgeInsets.fromLTRB(5, 0, 5, 0), width: 70, height: 70,),
]),
TableRow(children: [
Container(color: Colors.blue[400], margin: EdgeInsets.fromLTRB(5, 0, 5, 0), width: 70, height: 70,),
Container(color: Colors.blue[400], margin: EdgeInsets.fromLTRB(5, 0, 5, 0), width: 70, height: 70,),
Container(color: Colors.blue[400], margin: EdgeInsets.fromLTRB(5, 0, 5, 0), width: 70, height: 100,),
])
],
//橫向?qū)挾妊馀迹瑱嘀兀@里這樣寫分別占總寬度的1/4, 2/4, 1/4
columnWidths: {0: FlexColumnWidth(1),1: FlexColumnWidth(2),2: FlexColumnWidth(1)},
// columnWidths: {0: FixedColumnWidth(100),1: FixedColumnWidth(70),2: FixedColumnWidth(130)},//具體寬度洒闸,這里這樣寫寬度分別為100染坯,70,130
defaultVerticalAlignment: TableCellVerticalAlignment.middle, //縱向的對齊方式
);
}
主要屬性:
children:子控件丘逸,參數(shù)是一個List<TableRow>单鹿,TableRow的子控件是一個List<Widget>,這樣就構成了一個二維數(shù)組來排列子控件深纲。List<Widget>具體布局每一行仲锄,List<TableRow>再把這每一行羅列起來,構成一個矩陣布局湃鹊。
columnWidths:每一列的寬度儒喊,參數(shù)是一個Map<int, TableColumnWidth>,它規(guī)定了每一列展示的寬度币呵。這里提供兩種常用的寫法:
- columnWidths: {0: FixedColumnWidth(100),1: FixedColumnWidth(70),2: FixedColumnWidth(130)} 怀愧,寬度固定,這樣寫第1余赢、2芯义、3列的寬度分別是100,70妻柒,130扛拨;
- columnWidths: {0: FlexColumnWidth(1),1: FlexColumnWidth(2),2: FlexColumnWidth(1)},按比例分配寬度蛤奢,這里很像Android中的權重(Weight)鬼癣。這樣寫總共將寬度分為4份,第一列占1/4啤贩,第二列占2/4待秃,第三列占1/4;
border:背景邊框痹屹,設置邊框線章郁,圓角之類。
defaultVerticalAlignment:縱向的對齊方式,參數(shù)是TableCellVerticalAlignment的幾個固定值暖庄,top聊替、middle、bottom培廓、baseline惹悄、fill(填滿高度);
defaultColumnWidth:默認每列寬度肩钠,統(tǒng)一設定每一行的寬度泣港,按官方文檔建議的值寫 FlexColumnWidth(1.0),那就是均分价匠,如果你寫成一個固定值 FixedColumnWidth(70)当纱,那就是每列寬度都是70。
GridView
網(wǎng)格布局踩窖,名字個Android的GridVie一樣坡氯,功能也基本一樣。
基本使用:
Widget build(BuildContext context) {
return GridView(
scrollDirection: Axis.vertical,
reverse: false,
controller: scrollController,
// primary: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, //橫軸三個子widget
childAspectRatio: 1.3 //寬高比為1
),
children: <Widget>[
Container(color: Colors.blue[300], margin: EdgeInsets.fromLTRB(5, 5, 5, 5), width: 70, height: 70,),
Container(color: Colors.blue[300], margin: EdgeInsets.fromLTRB(5, 5, 5, 5), width: 70, height: 70,),
Container(color: Colors.blue[300], margin: EdgeInsets.fromLTRB(5, 5, 5, 5), width: 70, height: 70,),
Container(color: Colors.blue[300], margin: EdgeInsets.fromLTRB(5, 5, 5, 5), width: 70, height: 70,),
Container(color: Colors.blue[300], margin: EdgeInsets.fromLTRB(5, 5, 5, 5), width: 70, height: 70,),
Container(color: Colors.blue[300], margin: EdgeInsets.fromLTRB(5, 5, 5, 5), width: 70, height: 70,),
],
);
}
主要屬性:
scrollDirection: 滾動方向洋腮,Axis.vertical(豎直方向)箫柳;Axis.horizontal(水平方向);
reverse:子控件排序是否反轉;
controller: 滾動控制器徐矩,監(jiān)聽和控制滾動用到滞时,比如寫下拉加載更多之類的功能就可以用到這個叁幢÷说疲可以參考ScrollController 滾動監(jiān)聽及控制。
shrinkWrap:該屬性表示是否根據(jù)子組件的總長度來設置GridView的長度曼玩,默認值為false 鳞骤。默認情況下,GridView的會在滾動方向盡可能多的占用空間黍判。當GridView在一個無邊界(滾動方向上)的容器中時豫尽,shrinkWrap必須為true。
gridDelegate: 控制子控件排列規(guī)則的參數(shù)顷帖,包括配置子控件橫縱方向間距美旧,縱軸方向子控件個數(shù),子控件寬高比例等參數(shù)贬墩,詳細文檔參考GridView榴嗅。
然后還有幾個不太被關注的屬性:
primary: bool型,為true時陶舞,表示當子控件沒有超出GridView的尺寸時嗽测,它依然是可滾動的,當scrollDirection是Axis.vertical并且controller不為null時它的默認值是true(試了一下當controller不為null肿孵,手動將primary設置為true的話會報錯)唠粥。這個感覺不好理解疏魏,我試了一下,如果在子控件沒有超出父控件尺寸時晤愧,把這個設置為true大莫,效果如下:
拖動的時候會有這個拉到盡頭的反饋效果,但好像對實際使用沒啥影響官份。蘋果手機沒有試過葵硕,Android設備上這個屬性應該不太重要。
cacheExtent:設置預加載的區(qū)域贯吓, cacheExtent 設置為 0.0懈凹,則關閉了“預加載”。
addAutomaticKeepAlives: 該屬性表示是否將列表項(子組件)包裹在AutomaticKeepAlive 組件中悄谐;典型地介评,在一個懶加載列表中,如果將列表項包裹在AutomaticKeepAlive中爬舰,在該列表項滑出視口時它也不會被GC(垃圾回收)们陆,它會使用KeepAliveNotification來保存其狀態(tài)。如果列表項自己維護其KeepAlive狀態(tài)情屹,那么此參數(shù)必須置為false坪仇。(參考Flutter中文網(wǎng)的資料)
addRepaintBoundaries: 該屬性表示是否將列表項(子組件)包裹在RepaintBoundary組件中。當可滾動組件滾動時垃你,將列表項包裹在RepaintBoundary中可以避免列表項重繪椅文,但是當列表項重繪的開銷非常小(如一個顏色塊惜颇,或者一個較短的文本)時皆刺,不添加RepaintBoundary反而會更高效。和addAutomaticKeepAlive一樣凌摄,如果列表項自己維護其KeepAlive狀態(tài)羡蛾,那么此參數(shù)必須置為false宜鸯。(參考Flutter中文網(wǎng)的資料)
IndexedSemantics: 關于Semantics(語義)睛蛛,這里有一篇文章講得稍微詳細一點,Flutter中的Semantics上祈。
ListView
列表控件器予,這個在實際開發(fā)過程中使用的最多了浪藻。
這里寫法基本分為兩類,第一類寫的很死劣摇,適合列表項比較固定的情況:
ListView(
shrinkWrap: true,
padding: const EdgeInsets.all(20.0),
children: <Widget>[
const Text('I\'m dedicating every day to you'),
const Text('Domestic life was never quite my style'),
const Text('When you smile, you knock me out, I fall apart'),
const Text('And I thought I was so smart'),
],
);
這樣寫基本上列表的數(shù)目和內(nèi)容都是固定的珠移,實際情況中應該很少會用這種寫法。大多數(shù)實際需求中,列表展示的具體內(nèi)容是未知的钧惧,列表的條數(shù)也是未知的暇韧,甚至有時候稍微復雜的列表,根據(jù)不同的數(shù)據(jù)類型浓瞪,它的item布局都不相同懈玻,只有在獲取到具體的數(shù)據(jù)之后,我們才知道這些信息乾颁,所以為了靈活的處理和展示列表數(shù)據(jù)涂乌,下面兩種寫法比較多一點:
1. ListView.builder
ListView.builder(
itemCount: data.length,
itemExtent: 50.0, //強制高度為50.0
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text("$index"));
}
);
itemBuilder作為列表項的布局構建器,具體構建每個位置上的item布局英岭,并展示相應的內(nèi)容湾盒。itemCount配置列表的item條數(shù),可以根據(jù)具體的數(shù)據(jù)來靈活配置诅妹。
2. ListView.separated
相對于ListView.builder罚勾,ListView.separated增加了一個分割線的構造器separatorBuilder,分割線在實際開發(fā)中也是很常用到的吭狡。
Widget build(BuildContext context) {
return ListView.separated(
itemBuilder: (BuildContext context, int index) {
return Container(
height: 60,
child: FlatButton( onPressed: () {}, child: Text(menus[index],)));
},
separatorBuilder: (BuildContext context, int index) {
return Container(height: 1,color: Color(0xffe2e8ed),);
},
itemCount: menus.length);
}
ListView主要屬性:
itemExtent: 此參數(shù)如果不為null尖殃,則會強制children的“長度”為itemExtent的值;這里的“長度”是指滾動方向上子組件的長度划煮,也就是說如果滾動方向是垂直方向送丰,則itemExtent代表子組件的高度;如果滾動方向為水平方向弛秋,則itemExtent就代表子組件的寬度器躏。在ListView中,指定itemExtent比讓子組件自己決定自身長度會更高效铐懊,這是因為指定itemExtent后邀桑,滾動系統(tǒng)可以提前知道列表的長度,而無需每次構建子組件時都去再計算一下科乎,尤其是在滾動位置頻繁變化時(滾動系統(tǒng)需要頻繁去計算列表高度);
itemCount: item條數(shù)贼急;
itemBuilder茅茂、separatorBuilder的作用在上面代碼中都能看得比較清晰;
其他的包括scrollDirection太抓、reverse空闲、controller、shrinkWrap走敌、addRepaintBoundaries碴倾、addAutomaticKeepAlives等等這些基本上和GridView是一樣的,直接參考上面GridView就可以了。
SingleChildScrollView
單一子控件的滾動布局跌榔,跟Android的ScrollView基本一樣异雁,Android的ScrollView也是只能有一個子控件,不然就會報錯僧须。
基本使用很簡單:
Widget build(BuildContext context) {
return SingleChildScrollView(
scrollDirection: Axis.vertical,
reverse:false,
controller:controller,
child: Column(
children: <Widget>[
//...
],
),
);
}
彈窗
在Flutter中纲刀,對話框會有兩種風格,調(diào)用showDialog()方法展示的是material風格的對話框担平,調(diào)用showCupertinoDialog()方法展示的是ios風格的對話框示绊。
而這兩個方法其實都會去調(diào)用showGeneralDialog()方法,可以從源碼中看到最后是利用Navigator.of(context, rootNavigator: true).push()一個頁面暂论。
所以面褐,反正都是跳一個頁面,那么當需要自定義自己的彈窗的時候取胎,直接自己寫一個彈窗樣式的頁面盆耽,調(diào)用showDialog()跳過去就完了。
下面是兩個基本的彈窗模板
SimpleDialog
void _showSimpleDialog(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
return SimpleDialog(
title: Text('SimpleDialog'),
titlePadding: EdgeInsets.fromLTRB(20, 20, 20, 10),
contentPadding: EdgeInsets.fromLTRB(20, 10, 20, 20),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8))), //圓角矩形
children: <Widget>[
SimpleDialogOption(
child: Text('button1'),
onPressed: () {
WidgetUtils.showToast('button1');
},
),
SimpleDialogOption(
child: Text('button2'),
onPressed: () {
WidgetUtils.showToast('button2');
},
)
],
);
});
}
效果是這樣:
AlertDialog
void _showAlertDialog(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('AlertDialog'),
content: Text('這是一個AlertDialog'),
titlePadding: EdgeInsets.fromLTRB(20, 20, 20, 10),
contentPadding: EdgeInsets.fromLTRB(20, 10, 20, 20),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8))),
//圓角矩形
actions: <Widget>[
RaisedButton(
child: Text('button1'),
color: Colors.white,
disabledColor: Colors.white,
),
RaisedButton(
child: Text('button2'),
color: Colors.white,
disabledColor: Colors.white,
),
],
);
});
}
效果如下:
BottomSheet
從底部彈窗扼菠,可以直接調(diào)用showModalBottomSheet方法:
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
height: 150,
color: Colors.white,
child: Column(
children: <Widget>[
Container(
child:FlatButton(child:Text('item1', style: TextStyle(fontSize: 17)),onPressed: (){},color: Colors.white,),
alignment: Alignment.center,
height: 50,
),
Container(
child:FlatButton(child:Text('item2', style: TextStyle(fontSize: 17)),onPressed: (){},color: Colors.white,),
alignment: Alignment.center,
height: 50,
),
Container(
child:FlatButton(child:Text('item3', style: TextStyle(fontSize: 17)),onPressed: (){},color: Colors.white,),
alignment: Alignment.center,
height: 50,
),
],
));
}
)
上面的代碼效果如下:
按模板彈窗實際使用應該比較少摄杂,用到的屬性也比較簡單,看文檔基本都能明白循榆。
然后看看自定義彈窗析恢,其實也很簡單。直接調(diào)用showDialog()方法秧饮,里面的布局自己隨意寫映挂,背景,樣式盗尸,窗體內(nèi)容柑船,窗體位置等等等等,自由發(fā)揮泼各。下面是一個簡單的樣板鞍时。
void _showMyDialog(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.pop(context);//點擊外部陰影 彈窗消失
},
child: Container(
color: Colors.transparent,
alignment: Alignment.center,
child: GestureDetector(
onTap: () {},//點擊彈窗主體,自定義事件扣蜻,覆蓋父類的點擊事件逆巍,避免彈窗消失
child: Container(
width: 240,
height: 160,
color: Colors.white,
alignment: Alignment.center,
child: Text(
'我是自定義的diaolg',
style: TextStyle(fontSize: 15,color: Colors.black,decoration: TextDecoration.none),
),
),
),
));
});
}
效果:
由于自定義了點擊事件,點擊外部陰影彈窗會消失莽使,點擊內(nèi)部控件锐极,可以根據(jù)需求作各種處理,然后內(nèi)部的內(nèi)容可以自主控制芳肌,靈活方便灵再。
尾聲
本來以為三篇基本上可以列出常用控件肋层,現(xiàn)在看來還不夠。接下來還有Drawer翎迁,BottomNavigationBar栋猖、NestedScrollView配合TabBar等做一些常見效果,后面再繼續(xù)梳理鸳兽。這篇筆記先寫到這里掂铐。
以上。