Codelab: Flutter 佈局基礎教程
請注意,本文件已在官方文件中被移除,新的頁面正在翻譯中,本頁面作為臨時替代頁面,將在新頁面翻譯結束之後刪除,請勿在站外引用或分享以避免不好的使用者體驗,謝謝!
Row
and Column
是 Flutter 世界非常重要的兩個 widgets。想要把一個帶 label 的 Text
widget 放到另一個具有相應值的 Text
widget邊上?使用一個 Row
。想要現實多對 labels 和值?使用一個包含多個 Row
的 Column
。包含多個欄位的表單,旁邊有圖示的選單選項,旁邊有搜尋欄的按鈕,這些都是要用到 Row
s 和 Column
的地方。
codelab 將帶你瞭解 Row
s and Column
如何工作。因為它們非常相似,一旦你學會瞭如何使用 Row
,codelab 將會向你展示如何把相同的概念應用於 Column
。使用內建的編輯器,你將會邊玩邊測試你學到的知識。
從一個 Row 和一些 children開始
Row
or Column
的主要功能就是包含其他的 widgets,這些 widgets 被稱為 children。在一個 Row
裡,所有的 children 都會根據 text 方向從頭到尾水平排列。如果你的裝置被設定為英文或其他從左到右的語言,就會從左開始,如果你使用阿拉伯語或者其他從右到左顯示的語言,就會從右到左排列。
程式碼例子
下面是一個叫作 MyWidget
的 widget,在其內部建立了一個 Row
,然後請試著將三個 BlueBox
widgets 加到 Row
的 children中。
主軸空間
Row
的主軸是指水平方向的軸(Column
的主軸是指豎直方向的)。每一個 Row
都有一個叫 mainAxisSize
的屬性,它決定了此 Row
沿著水平方向佔用空間的大小。預設情況下,mainAxisSize
的值為 MainAxisSize.max
,這意味著 Row
將會佔用所有可用的水平方向空間。你可以使用 MainAxisSize.min
實現讓一個 Row
佔用儘可能少的空間。
程式碼例子
這裡的例子是你剛剛完成的。試著將 Row
的 mainAxisSize
的值設為 MainAxisSize.min
,看看會發生什麼。
主軸對齊
如果你將一個 Row
的 mainAxisSize
設為最小值,在 children 之外就不會有更多的空間。如果你將其設為最大值,Row
就會有多出來的空間。你可以使用 mainAxisAlignment
屬性來控制
Row
中的 children 對齊的方式。
MainAxisAlignment
有六種不同的列舉值:
-
將所有的 children 儘可能向
Row
的 start 方向排列(如果是從左到右,那就是靠左排列)。 -
將所有的 children 儘可能向
Row
的 end 方向排列。 -
將 children 聚在
Row
主軸的中間位置。 -
將主軸空白位置進行均分,用來在 children 之間製造間隔,首尾 children 距邊緣沒有間隙。
-
很像
spaceBetween
,除了讓首尾 children 距邊緣也有相同的間隙。 -
很像
spaceEvenly
,只是首尾 children 距邊緣間距為中間 children 間距的一半。
程式碼例子
下面的 row 的 mainAxisAlignment
被設為了 start。試著將其改為其他的值,然後重新執行看看會怎麼樣。
交叉軸對齊
Row
widgets 的交叉軸是豎直方向的軸,你可以用 crossAxisAlignment
屬性來控制
children 如何在垂直方向排列。預設值是 CrossAxisAlignment.center
,一共有五種值:
-
將所有的 children 向
Row
豎直方向的 start 方向排列(如果是從上到下,你可以修改verticalDirection
來改變)。 -
將所有的 children 向
Row
豎直方向的 end 方向排列(預設是底部)。 -
將 children 聚在
Row
豎直方向軸的中間位置。 -
所有的 Children 的高度會被拉伸到和
Row
一樣,填滿豎直方向軸的空間。 -
所有的 Children 的 baselines 在豎直方向對齊。
程式碼例子
Row
有兩個小的 children 和一個大的。crossAxisAlignment
屬性預設為 center。可以試著將其變為其他值然後重新執行,看看會怎樣。
會有一個警告: CrossAxisAlignment.baseline
requires
that another property be set as well, so you
will see an error if you try that one. 不用擔心,在下一節將會對此進行討論。
基線對齊
Sometimes it’s handy to align widgets containing text not by their
overall bounds, but by the baselines used by their characters.
That’s what CrossAxisAlignment.baseline
is for. You
can use it in combination with a Row
’s textBaseline
property (which indicates which baseline to use) to align a
Row
’s children along their baselines.
有時候根據包含文字的 widgets 的基線對齊是比較方便的,而不是根據它們的整體邊框對齊。那就是 CrossAxisAlignment.baseline 的用途。你可以使用聯合使用 Row
的 textBaseline
屬性(決定按照哪種基線來對齊),來決定 Row
的所有 children 根據基線對齊。
程式碼例子
row 裡包含三個擁有不同字型大小的 Text
widgets。試著將 crossAxisAlignment
屬性設為 baseline
,然後試驗 textBaseline
的不同值(TextBaseline
列舉值裡包含可用的 baseline 值)。
可伸縮 children
到目前為止,例子中所有用作 children 的 widgets 都有一個固定的大小。不過 Row
可以讓它的 children 可伸縮,來適應可用的空間。為了更好的理解這是怎麼回事兒,最好看看 Row
的大小和它的 children。
-
首先,
Row
首先會要求它所有的 children 想要多大的尺寸。 -
然後,它會計算主軸(水平)的剩餘空間。
-
然後它把剩下的空間根據 children 的 flex 值分給它的可伸縮的 children,這些可伸縮的 children 可以使用他們提供的部分或者全部的空間。
-
在那時,
Row
知道所有的 children 的尺寸有多大,然後可以根據你之前學到的 axis size 和 alignment 屬性來排列它們。
大多數 widgets 是固定大小的。你可以將他們包裹在一個 Flexible
widget 中來將它們變為可伸縮的。
Flexibles
有兩個重要屬性: flex
值決定與其他 children 相比可佔用剩餘空間的多少, fit
屬性決定其 child 是否佔用所有額外的空間。
程式碼例子
試著將 row 中間的 box 包裹在一個 flex
factor 為 1 並且 fit
為 FlexFit.loose
的 Flexible widget中。然後試著將
fit 改為
FlexFit.tight`,看看會發生什麼。
flex
factor 為 1 和 fit
為 FlexFit.tight
的組合是非常常見的, 更簡單的方式是直接使用 Expanded
widget.
Flex factors
如果 Row
或 Column
中多個 children 都是可伸縮的,那麼如何分配可用空間取決於它們的 flex
值。每個 child 獲得的空間將取決於他們的 flex 值佔所有 children 的 flex 值之和的比例。
remainingSpace * (flex / totalOfAllFlexValues)
例如,如果有兩個 flex 值為 1 的 children,每個將獲得一半的可用空間。如果有兩個 flex 值為 1 的 children,還有一個 flex 值為 2 的 child,那麼前兩個 children 將各獲得四分之一的可用空間,另一個 child 將獲得一半的可用空間。
程式碼例子
在這個例子中, Row
的所有三個 children 都是可伸縮的,試著改變它們的 flex
值然後重新執行看看它們的尺寸如何改變。
如果沒有空間了怎麼辦?
正如你所看到的,當一個 Row
問它其中一個可伸縮的 child 想要多大空間時,它會根據這個 child 的 flex
值分配給它一個最大值。但是固定大小的 children 沒有這個限制,它們可以自己決定大小。
一個副作用就是,無法阻止一個固定大小的 child 宣告超出 Row
所能支援的大小。當這種情況發生時,就會發生溢位。你可以透過修改這個 child 的大小或者使用一個可捲動的 widget 來解決這個問題。
程式碼例子
下面的 Row
包含一個特別寬的 widget。執行程式碼看會發生什麼,然後試著修改Container
的寬度使其適應。
試著使用 SizedBox 來留出空間
如果你需要在一個 Row
中的兩個 children 之間指定一個特定的間隔,一個簡單的方法是在中間放一個寬度合適的 SizedBox
。
程式碼例子
試著用一個寬度 100 的 SizedBox
在兩個 items 中間製造一些間隔。
Spacers 留出可變空間
使用Spacers
是另一個在 Row
的 children 之間留出空間的方法。它們是可伸縮的,可以填滿任何剩下的空間。
程式碼例子
試著在第一個和第二個 children 之間加一個 Spacer
。
等等, 我不是還要學習 Columns 嗎?
給你個驚喜,你已經學習了。Row
的所有用法和 Column
是一樣的,只是維度不同。
Row
的主軸是水平的,而 Column
的主軸是豎直的,但是它們設定其 children 的大小和位置的方式是一樣的。它們還共用一個基本類 Flex
。所以你已經學習的有關 Row
的用法,同樣適用於 Column
。
程式碼例子
這裡有一個包含不同尺寸和一些重要屬性已經設定好的 children 的 Column
。試著擺弄以下,你會發現 Column
就像一個豎過來的的 Row
。
將它們放在一起
現在你已經熟悉了 Row
和 Column
的重要屬性,你已經可以來聯絡將它門組合在一起來建立使用者介面。下面的例子將帶你完成一個名片顯示的建立。
程式碼例子
每一張名片都需要一個名字和頭銜,讓我們從這裡開始。
-
新增一個
Column
widget -
新增兩個 text widgets 到
Column
的 children 清單中:-
第一個是名字(簡短一點更適合於一個小視窗),使用
headline
樣式:
-
style: Theme.of(context).textTheme.headline
-
第二個 text widget 應該是
Experienced App Developer
,使用預設樣式(不用設定style
屬性)。 -
設定
Column
的crossAxisAlignment
為 start,使得 text widgets 會開始對齊,而不是居中。 -
將
Column
的mainAxisSize
設為MainAxisSize.min
,這樣 card 才不會擴充套件到整個 window 那麼高。
名片的左上角通常會有一個圖示或者標誌,所以下一步是加一個到你的名片上。將你剛建立的 Column
包裹在一個 Row
widget 中。
Row(
children: [
Column( … ), // <- This should be the Column you made in the previous step
],
);
現在你可以新增一個圖示:
-
在你的
Row
的Column
的前面,加一個Padding
widget。-
設定
padding
為const EdgeInsets.all(8)
。 -
將一個
Icon
widget 作為Padding
widget 的 child。-
你可以使用任何 icon resource,
Icons.account_circle
看起來就不錯。 -
把
Icon
的大小設定為 50。
-
-
你的第一個 Row
現在完成了。還有兩件事要做,你需要一個 Column
把它們放進去。把你的 Row
包裹進一個 Column
widget 就像這樣:
Column(
children: [
Row( … ), // <- This should be the Row with your Icon and Text widgets.
],
);
然後按照以下步驟完成你的新 Column
:
-
設定
Column
的 mainAxisSize 為最小-
否則它會充滿整個螢幕!
-
-
設定
Column
的crossAxisAlignment
為 stretch。-
這使得所有 children 都會拉伸到最大寬度
-
-
在
Column
中你的Row
下面新增更多的 widgets:-
一個高度為 8 的
SizedBox
-
一個空
Row
(沒有 children 或其他屬性) -
一個高度為 16 的
SizedBox
-
另一個空
Row
-
現在就差幾步了。接下來是第二個 Row
。新增以下的 widgets 作為它的 children:
-
一個地址為 ‘123 Main Street’ 的
Text
widget -
一個電話為 ‘800-123-1234’ 的
Text
widget
如果你現在執行程式碼,你會看到這兩個 Text
widgets 是挨著的,而不是在 Row
的兩端對齊,這是不對的。你可以將 Row
的 mainAxisAlignment
設為 spaceBetween
,使得這兩個 Text
widge 中間有些間隔。
最後一步是在名片的底部放一些圖示。
-
新增四個
Icon
widgets 到最後一個Row
中。你可以使用任何你喜歡的圖示資源,但是以下圖示是一個很好的選擇,用來展示你想象中的關注於 accessibility, fast development, and multi-platform apps 的開發人員:Icons.accessibility
Icons.timer
Icons.phone_android
Icons.phone_iphone
-
設定
Row
的mainAxisAlignment
屬性為MainAxisAlignment.spaceAround
。