Flutter基礎控件篇[3]

前言

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大莫,效果如下:

primary: 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空闲、controllershrinkWrap走敌、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');
                },
              )
            ],
          );
        });
  }

效果是這樣:


SimpleDialog
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,
              ),
            ],
          );
        });
  }

效果如下:


AlertDialog
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,
            ),
          ],
        ));
  }
)

上面的代碼效果如下:


BottomSheet

按模板彈窗實際使用應該比較少摄杂,用到的屬性也比較簡單,看文檔基本都能明白循榆。
然后看看自定義彈窗析恢,其實也很簡單。直接調(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ù)梳理鸳兽。這篇筆記先寫到這里掂铐。
以上。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末揍异,一起剝皮案震驚了整個濱河市全陨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌衷掷,老刑警劉巖辱姨,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異戚嗅,居然都是意外死亡雨涛,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門懦胞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來替久,“玉大人,你說我怎么就攤上這事躏尉◎歉” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵胀糜,是天一觀的道長颅拦。 經(jīng)常有香客問我,道長教藻,這世上最難降的妖魔是什么距帅? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮括堤,結果婚禮上碌秸,老公的妹妹穿的比我還像新娘。我一直安慰自己痊臭,他們只是感情好哮肚,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著广匙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪恼策。 梳的紋絲不亂的頭發(fā)上鸦致,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天潮剪,我揣著相機與錄音,去河邊找鬼分唾。 笑死抗碰,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的绽乔。 我是一名探鬼主播弧蝇,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼折砸!你這毒婦竟也來了看疗?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤睦授,失蹤者是張志新(化名)和其女友劉穎两芳,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體去枷,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡怖辆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了删顶。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竖螃。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖逗余,靈堂內(nèi)的尸體忽然破棺而出特咆,到底是詐尸還是另有隱情,我是刑警寧澤猎荠,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布坚弱,位于F島的核電站,受9級特大地震影響关摇,放射性物質(zhì)發(fā)生泄漏荒叶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一输虱、第九天 我趴在偏房一處隱蔽的房頂上張望些楣。 院中可真熱鬧,春花似錦宪睹、人聲如沸愁茁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鹅很。三九已至,卻和暖如春罪帖,著一層夾襖步出監(jiān)牢的瞬間促煮,已是汗流浹背邮屁。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留菠齿,地道東北人佑吝。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像绳匀,于是被迫代替她去往敵國和親芋忿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353