本文主要介紹了Flutter佈局相關的內容,對相關知識點進行了梳理,並從實際例子觸發,進一步講解該如何去進行佈局。html
在介紹Flutter佈局以前,咱們得先了解Flutter中的一些佈局相關的特性。git
box constraints有人也翻譯爲盒約束、箱約束,我我的仍是以爲邊界約束可能更直觀一些。github
Flutter中的邊界約束,是指widget能夠按照指定限定條件,來決定自身如何佔用佈局空間。Flutter借鑑了不少React相關的東西,包括一些佈局思想,可是它自身沒有抽離出佈局樣式,而是用不一樣的widget去實現不一樣的佈局,將樣式嵌入widget中,用戶能夠像搭積木同樣寫佈局,寫法上跟React很像,只不過沒了樣式的設定。segmentfault
這樣作的好處,我以爲多是爲了統一的渲染。加入樣式,會讓佈局複雜很多,在渲染層面會下降不少性能。所以,Flutter在大的方向上,加入不一樣類型的佈局widget。在小的方向上,只給出不多的定製化的東西,將佈局限定在有限的範圍內,在完成佈局的同時,讓整個渲染可以統一,加快了更新和渲染。bash
可是,缺點也是一樣明顯,少了不少靈活性,不一樣的佈局方式都被抽離出了widget,你們須要瞭解的widget很是多,增長了學習成本。架構
在Flutter中,widget是由其底層的RenderBox渲染,渲染邊界的約束(Constraints)由父級給出,widget在這些約束下調整自身尺寸。約束包括最小最大寬高,尺寸則是具體的寬高。less
在Android中,佈局的寬高限定有三種,match_parent、wrap_content以及具體尺寸。在Flutter中,也有這三種約束。ide
可是,Flutter並無把widget處理的這麼絕對,這些約束條件包含在widget裏,不像Android能夠在外面去指定。所以,一些widget,例如Container,會根據參數的不一樣,約束條件也不一樣。Container默認是儘量大的,可是給定尺寸的話,就會優先使用具體值。不一樣的widget可能設置條件不一樣、其子widget不一樣,約束條件也會不同。Flutter將每種widget限制在不一樣的約束範圍裏,實際佈局的時候,還須要綜合去考慮。函數
按照約束條件來分類,不少widget不太好區分開來,官方也是根據其子元素的個數來分類。佈局
18
種(目前是2018年5月25日,後續我想確定會增長的,下面相似);11
種;其中平常中用的多的,例如Container、Padding、Center、Align、Row、Column、Stack、ListView等也有上十種。
Flutter提供接近30多種不一樣的佈局widget,仍是源於其對widget的定位(在此處再也不闡述,想了解的,能夠翻看筆者以前文章的介紹)。對比其餘移動端的開發平臺,能夠看出Flutter的佈局widget是巨多,這也是爲何Flutter如今學習曲線很長的一個緣由。
先來講下優勢,統一渲染,更新效率更高。可是,對於普通開發者而言,是不會去太在意這些的。性能高原本就是平臺應該提供的最基本的能力,難道不是你應該提供的嗎?
而後說下缺點吧,掌握起來仍是很是費事的,佈局起來也是挺蛋疼的。常規的佈局還好,一到複雜的佈局,以爲這個也能實現,那個也能實現,最後不知道哪一個能夠實現。
Flutter對於性能的優化,把平臺側的一些成本轉接到開發者身上,不過呢,如今也是Flutter的初期,能夠看出,總體的設計思路仍是很是好的,也只有谷歌這種輪子大廠纔敢這麼幹。可是,很明顯少了些人爲關懷,不一樣widget間約束條件穿插着,也能夠說Flutter佈局控件種類的增長,是其不斷的打補丁致使的,後續的各類helper,也是爲了填坑,這一起Flutter顯然沒有處理的很好。
可是,凡事都有個過程,不能說Flutter這些地方作的很差,只是目前看起來比較混亂,理想的架構設計,落地下來,可能就不是那麼簡單,開發者的需求千差萬別,有了生態,什麼都好說,固然這個過程,預計是會很是的緩慢。
在Flutter中,咱們平時自定義的widget,通常都是繼承自StatefulWidget或StatelessWidget(並非只有這兩種),這兩種widget也是目前最經常使用的兩種。若是一個控件自身狀態不會去改變,建立了就直接顯示,不會有色值、大小或者其餘屬性的變化,這種widget通常都是繼承自StatelessWidget,常見的有Container、ScrollView等。若是一個控件須要動態的去改變或者相應一些狀態,例如點擊態、色值、內容區域等,那麼通常都是繼承自StatefulWidget,常見的有CheckBox、AppBar、TabBar等。其實單純的從名字也能夠看出這兩種widget的區別,這兩種widget都是繼承自Widget類。
Widget類在Flutter中是很是重要的,繼承自Widget類的有PreferredSizeWidget、ProxyWidget、RenderObjectWidget、StatefulWidget、StatelessWidget。咱們平常使用的絕大部分widget都是繼承自Widget類,
查看Widget類源碼,內部實現很是簡單,構造函數以下
const Widget({ this.key });
final Key key;
複製代碼
這個key的做用,注視上寫的很清楚,是用來控制在widget樹中替換widget的時候使用的。其中Key類是Widget、Element以及SemanticsNode的惟一標識符,繼承自Key的還有LocalKey以及GlobalKey。
在說到StatefulWidget以前,先說下State。State的做用有兩點:
State的生命週期有四種狀態:
完整生命週期以下:
State中比較重要的一個方法是setState
,當修改狀態時,widget會被更新。比方說點擊CheckBox,會出現選中和非選中狀態之間的切換,就是經過修改狀態來達到的。
查看setState源碼,在一些異常的狀況下將會拋出異常:
檢查完一系列異常後,最後調用代碼以下:
_element.markNeedsBuild();
複製代碼
markNeedsBuild內部,則是經過標記element爲diry,在下一幀的時候重建(rebuild)。能夠看出setState並非當即生效,它只是將widget進行了標記,真正的rebuild操做,則是等到下一幀的時候纔會去進行。
StatefulWidget和StatelessWidget以下所示
一個StatelessWidget能夠用多個不一樣的BuildContext構建,而一個StatefulWidget會爲每一個BuildContext建立一個State對象。
對於StatelessWidget,build方法會在以下三種狀況下調用,
class GreenFrog extends StatelessWidget {
const GreenFrog({ Key key }) : super(key: key);
@override
Widget build(BuildContext context) {
return new Container(color: const Color(0xFF2DBD3A));
}
}
複製代碼
StatefulWidget的兩個主要類別:
class YellowBird extends StatefulWidget {
const YellowBird({ Key key }) : super(key: key);
@override
_YellowBirdState createState() => new _YellowBirdState();
}
class _YellowBirdState extends State<YellowBird> {
@override
Widget build(BuildContext context) {
return new Container(color: const Color(0xFFFFE306));
}
}
複製代碼
每一個頁面設計都不同,相同頁面可選擇的佈局方式也不同,若是單純的說應該如何去佈局,我以爲不現實,你們能夠參考下Flutter官方的佈局教程。接下來,筆者,經過一個簡單的頁面,來一步一步的拆解佈局的流程。整個過程,基本上按照拆解、組件封裝、具體佈局這三步來的。
根據設計圖,能夠看出總體時分行展現的,所以最外層是一個Column元素
每一行之間的間隔,則能夠考慮用Padding或者Container來設置。
經過上面這樣一步一步的分析後,基本上對大體的佈局有了一個瞭解,最外層的控件大體選對(只要能實現的話,就是複雜度以及效率的問題),而後一步一步的拆解每一行的元素,若是有重複的或者以爲能夠封裝出來的部分,則進行下一步。
每一行的拆解,大體也是按照這個思路來進行,所以筆者在這裏就不作講解了。
例如上面,筆者想對第四行的這種展現進行封裝,以爲從此的佈局可能會用到,所以在這一步,能夠先把這一起抽離出一個控件。利用Row的mainAxisAlignment以及Expanded來實現這種效果,具體的實現筆者再也不詳細的描述了。
通過這一步,總體的規劃設計圖已經有了,各個組件也都有了,接下來的工做就是組裝了。
具體佈局設計到一些細節的地方,例如間隔(Padding或者Container)、居左居右居中(Align)、點擊事件(GestureDetector)以及圓角(ClipRRect)等一些特殊狀況,基本上就是嵌套,一層一層去實現。
在實際佈局中,筆者實際使用的是Scaffold,頂部的AppBar將陰影直接去掉便可實現效果,body部分則實現2-5行的內容。最外層套一個Column也能實現,本質上都沒什麼區別,運行效果圖以下所示。
筆者建了一個flutter學習相關的項目,Github地址,裏面包含了筆者寫的關於flutter學習相關的一些文章,會按期更新,也會上傳一些學習demo,歡迎你們關注。