Flutter Container Widget 佈局詳解

在Flutter中,號稱一切皆widget,手勢是Widget,動畫是Widget,UI更是Widget,今天咱們就來講說Widgets裏比較特殊的一個,Container。html

1. 參考文獻

Container初用起來很簡單,可是裏面的邏輯又有些複雜,我也不敢說徹底吃透,因此本文初期版本仍是以總結網上各類文章爲主,再加上本身的理解,若是有不對的地方,請必定指出android

Flutter快速上車之Widget函數

Container class佈局

Building Layouts in Flutterpost

Dealing with box constraints in Flutter動畫

Flutter — Container Cheat Sheetui

Widgets: Containerdebug

What is a Container in Flutter?3d

Container Widget with example Flutter Tutorialorm

Container Widget In Flutter

Understanding Flutter Layout (Box)Constraints

2. 介紹

在flutter中,全部的功能都被分散成單一功能的widget,好比居中有Center,邊框有Padding,文字是Text,手勢是GestureDetector,他們各自維護一個功能,可是咱們商業App的UI都很精美,若是要實現一個很好的佈局,須要嵌套很是多的佈局Widget,因此Container應運而生:

A convenience widget that combines common painting, positioning, and sizing widgets.

官方文檔一語道破了container複雜的緣由,它是一個便利部件,融合了繪圖、定位和大小部件,據我所知,能夠設置大小,背景顏色,邊框,圓角,陰影,漸變,並且大小能夠有不少種狀況,時而依賴於父部件,時而依賴於子部件,時而依賴於本身,因此咱們稍後會重點說一說Container的佈局(尺寸規則,寬高)。

3. Widget渲染流程

flutter是樹狀渲染結構,首先從根結點開始渲染,從上到下傳遞約束,直到最終的葉子節點(沒有子節點了),而後葉子節點根據約束肯定自身大小,而後將大小返回給上級結點,而後上一級根據葉子節點的尺寸,決定本身的大小,再返回上一級,最終根節點肯定了大小。以後,根結點逐級往下襬放子節點的位置(根據子節點及子節點兄弟節點的大小和偏移量)。

4. Container渲染流程

根據官網的介紹,Container會首先使用設置的padding來圍繞子部件,而後對padding的大小添加額外的約束(若是非空),而後容器被外部的空白區域(margin)包圍。在繪製過程當中,Container首先應用變換(transform),而後繪製裝飾(decoration)來填充區域,接着繪製子部件,最後繪製前景裝飾(foregroundDecoration),同時填充該區域。

decoration和foregroundDecoration是填充配置,前者是在子部件之下,後者是子部件之上,能夠設置填充顏色,邊框,填充形狀,陰影,漸變色,背景圖片等。

若是Container的約束是有限制的,那麼沒有子部件的Container會嘗試儘量大,若是Container的約束是沒有限制的(unbounded),它就會盡量小。

有子部件的Container,根據子部件肯定本身的大小。

5. 有無限制約束

到底什麼是有限制約束和無限制約束呢,各類部件又都是哪一種約束呢?

在flutter中,widgets由底層的RenderBox渲染盒渲染,父組件向渲染盒提供約束條件,而後渲染盒用這些約束調整本身的尺寸,約束(Constraint)由最大和最小的寬高組成,尺寸(Size)由特定的寬高組成。

一般來講,有三種處理約束的盒子:

  • 儘量大:Center和ListView等
  • 和子部件同樣大:Transform和Opacity等
  • 特定大小:Image和Text等
  • 特殊狀況:Row和Column由其給定的約束決定,Container由其構造函數的參數決定

但約束有時會變得緊湊,意思是它沒有留給渲染盒子自行決定尺寸的餘地(例如最大寬度和最小寬度相等,那容許的寬度是個固定值),例如App這個Widget,它的約束被設置爲固定的應用程序內容大小(也就是整個屏幕)。而在Flutter中,不少的widget,尤爲是隻能有一個子部件的widget,會傳遞本身的約束到子部件。也就是說,若是你在App根渲染樹裏嵌套了一系列widget,他們將會由於緊湊的約束,一級級地貼着。

可是有些widget會使約束寬鬆,意思是最大的約束保留,可是最小的約束移除了,好比Center。

5.1 無限制約束

  • 在某些狀況下,賦給widget的約束是無限制(unbounded)的,或者說是無限(infinite)的。也就是說,最大寬度和最大高度,都是double.INFINITY。
  • 一個嘗試儘量大的widget,遇到無限制約束的時候,是不會起做用的,由於它不知道到底該有多大,在debug模式下,就會拋出異常。
  • 最多見的擁有無限制約束的狀況,就是嵌入在彈性盒子裏面,好比Row,Column或者能夠滾動的區域(ListView或者其餘ScrollView子類)。

須要指出的是,ListView會在其交叉方向擴張到父部件邊界,例如一個縱向滾動的列表,在橫向會盡可能和父部件同樣寬。 當你在橫向滾動列表裏,嵌入一個縱向滾動列表的時候,縱向列表會盡量寬,也就是無限寬,由於橫向列表是無盡寬的,這就會異常。

另外,彈性盒子(Row和Column)在有限制和無限制約束的狀況下,表現出來的行爲也不一樣。

  • 在有限制的約束時,他們會在其方向上儘量大。
  • 在無限制約束時,他們會在其方向上適應他們子組件(包住子組件)。這種狀況下,你不能在彈性盒子裏用Expanded,由於這是將沒法肯定部件大小。

6. Container佈局(尺寸規則)

由於Container集合了其餘部件的功能,因此它的佈局有些複雜,簡而言之,按照順序,Container會:

  • 遵循對齊規則
  • 爲子部件調整自身大小
  • 遵循寬高和約束
  • 而後Container嘗試儘量小。

6.1 來看看官方文檔的解釋(這個解釋看不懂就算了,有點囉嗦):

  • 若是Container沒有子部件,沒有寬高,沒有約束,而且父部件提供了無限制約束(unbounded constraints),Container會盡量小。
  • 若是Container沒有子部件,沒有對齊規則,可是提供了高度、寬度或者約束,那麼Container會在遵循寬、高、約束和父部件約束的狀況下,儘量小。
  • 若是Container沒有子部件,沒有寬高,沒有約束,沒有對齊,可是父部件提供了有限制約束,那麼Container會擴張以適應(fit)父部件約束
  • 若是Container有一個對齊規則,而且父部件提供了無限制約束,那麼Container會嘗試調整本身來包圍子部件
  • 若是Container有一個對齊規則,並且父部件提供了有限制約束,那麼Container會嘗試擴張以適應(fit)父部件,而後根據對齊方式,將子部件置於其內
  • 另外,Container有子部件,可是Container沒有寬高、約束、對齊規則,那麼Container會傳遞父部件的約束到其子部件,而後調整自身來匹配子部件。
  • margin和padding也會影響佈局,decoration會隱性增長padding(好比設置border)。
  • 默認是儘量大

6.2 咱們來總結一下:

maxWidth maxHeight 約束 有無子組件 佈局規則
有值 有值 有限制 儘量大
有值 無值 高度無限制,寬度有限制 高度儘量小,寬度儘量大
無值 有值 高度有限制,寬度無限制 高度儘量大,寬度儘量小
無值 無值 無限制 儘量小
都行 都行 都行 在知足約束的前提下儘量小

6.3 例子

沒有子組件,有約束,儘量大↓

沒有子組件,有約束,儘量大

沒有子組件,父組件約束最大寬度是屏幕寬度,最大高度是無限,本身的約束最小寬度是100,最小高度是100,則高度儘量小到100,寬度儘量大到屏幕寬度↓

父組件最大約束是屏幕寬高,本身是固定寬度100,則寬度是100,高度儘量大到屏幕高度↓

本身沒有設置約束,有子組件,因此本身包住子組件↓

本身設置固定高度100,寬度沒有約束,有子組件,則高度爲100,寬度包住子組件↓

有子組件,本身的高度是固定100,寬度設置爲無限,則高度爲100,寬度是父組件的約束屏幕寬度↓

有子組件,設置最小寬高爲無限,則大小爲屏幕大小↓

7. 總結

Container應該是flutter中最靈活的佈局widget,你們必定要善用Container,用巧妙的方式處理佈局,不然可能會讓代碼可讀性變差,難以維護

相關文章
相關標籤/搜索