版面配置
由於 Flutter 是一個 UI 工具包,您將花費大量時間使用 Flutter Widget 建立版面配置。在本節中,您將學習如何使用一些最常見的版面配置 Widget 來建構版面配置。您將使用 Flutter DevTools (也稱為 Dart DevTools) 來了解 Flutter 如何建立您的版面配置。最後,您將遇到並除錯 Flutter 最常見的版面配置錯誤之一,即可怕的「無界限約束」錯誤。
理解 Flutter 中的版面配置
#Flutter 版面配置機制的中心是 Widget。在 Flutter 中,幾乎所有東西都是 Widget — 即使是版面配置模型也是 Widget。您在 Flutter 應用程式中看到的圖片、圖示和文字都是 Widget。您看不到的東西也是 Widget,例如排列、約束和對齊可見 Widget 的 Row、Column 和網格。
您可以透過組合 Widget 來建立更複雜的 Widget,進而建立版面配置。例如,下圖顯示了 3 個圖示,每個圖示下方都有一個標籤,以及對應的 Widget 樹狀結構
在此範例中,有一個 3 個 Column 的 Row,每個 Column 都包含一個圖示和一個標籤。所有版面配置,無論多麼複雜,都是透過組合這些版面配置 Widget 來建立的。
約束條件
#了解 Flutter 中的約束條件是了解 Flutter 中版面配置運作方式的重要一環。
從廣義上講,版面配置是指 Widget 的大小及其在螢幕上的位置。任何給定 Widget 的大小和位置都受其父 Widget 的約束;它不能擁有它想要的任何大小,並且它也不能決定自己在螢幕上的位置。相反地,大小和位置是由 Widget 和其父 Widget 之間的「對話」決定的。
在最簡單的範例中,版面配置「對話」看起來像這樣
- Widget 從其父 Widget 接收其約束條件。
- 約束條件只是一組 4 個 double:最小和最大寬度,以及最小和最大高度。
- Widget 決定其在這些約束條件內應有多大,並將其寬度和高度傳回給父 Widget。
- 父 Widget 會查看它想要的大小以及應如何對齊,並相應地設定 Widget 的位置。可以使用各種 Widget (例如
Center
) 和Row
與Column
上的對齊屬性來明確設定對齊方式。
在 Flutter 中,此版面配置「對話」通常用簡化的短語來表達:「約束條件向下傳遞。大小向上傳遞。父 Widget 設定位置。」
Box 類型
#在 Flutter 中,Widget 是由其底層的 RenderBox
物件呈現的。這些物件決定如何處理傳遞給它們的約束條件。
一般來說,有三種 Box
- 那些試圖盡可能大的 Box。例如,
Center
和ListView
所使用的 Box。 - 那些試圖與其子 Widget 大小相同的 Box。例如,
Transform
和Opacity
所使用的 Box。 - 那些試圖成為特定大小的 Box。例如,
Image
和Text
所使用的 Box。
某些 Widget (例如 Container
) 會根據其建構函式引數而異。Container
建構函式預設為嘗試盡可能大,但如果您為它提供寬度,例如,它會嘗試遵循該寬度並成為該特定大小。
其他 Widget (例如 Row
和 Column
(彈性 Box)) 會根據它們被賦予的約束條件而異。在了解約束條件文章中閱讀更多關於彈性 Box 和約束條件的資訊。
配置單一 Widget
#若要在 Flutter 中配置單一 Widget,請使用可以更改其在螢幕上的位置的 Widget (例如 Center
Widget) 包裹可見的 Widget (例如 Text
或 Image
)。
Widget build(BuildContext context) {
return Center(
child: BorderedImage(),
);
}
下圖顯示一個未在左側對齊的 Widget,以及一個在右側居中的 Widget。
所有版面配置 Widget 都具有以下其中一個
- 如果它們採用單一子 Widget,則具有
child
屬性 — 例如,Center
、Container
或Padding
。 - 如果它們採用 Widget 清單,則具有
children
屬性 — 例如,Row
、Column
、ListView
或Stack
。
Container
#Container
是一個方便的 Widget,由多個負責版面配置、繪圖、定位和調整大小的 Widget 組成。在版面配置方面,它可用於為 Widget 新增邊距和間距。這裡也可以使用 Padding
Widget 來達到相同的效果。以下範例使用 Container
。
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(16.0),
child: BorderedImage(),
);
}
下圖顯示一個左側沒有間距的 Widget,以及一個右側有間距的 Widget。
若要在 Flutter 中建立更複雜的版面配置,您可以組合許多 Widget。例如,您可以組合 Container
和 Center
Widget build(BuildContext context) {
return Center(
Container(
padding: EdgeInsets.all(16.0),
child: BorderedImage(),
),
);
}
垂直或水平配置多個 Widget
#最常見的版面配置模式之一是垂直或水平排列 Widget。您可以使用 Row
Widget 水平排列 Widget,並使用 Column
Widget 垂直排列 Widget。本頁面上的第一張圖使用了這兩種 Widget。
這是使用 Row
Widget 最基本的範例。
Row
或 Column
的每個子 Widget 本身都可以是 Row 和 Column,組合起來構成一個複雜的版面配置。例如,您可以使用 Column 為上述範例中的每個圖片新增標籤。
在 Row 和 Column 中對齊 Widget
#在以下範例中,每個 Widget 的寬度為 200 像素,而視窗的寬度為 700 像素。因此,Widget 會從左到右依序對齊,而所有額外的空間都位於右側。
您可以使用 mainAxisAlignment
和 crossAxisAlignment
屬性來控制 Row 或 Column 如何對齊其子 Widget。對於 Row,主軸水平延伸,而交叉軸垂直延伸。對於 Column,主軸垂直延伸,而交叉軸水平延伸。
將主軸對齊方式設定為 spaceEvenly
會在每個圖片之間、之前和之後均勻分割可用水平空間。
Column 的運作方式與 Row 相同。以下範例顯示一個包含 3 個圖片的 Column,每個圖片的高度為 100 像素。渲染 Box (在本例中,為整個螢幕) 的高度大於 300 像素,因此將主軸對齊方式設定為 spaceEvenly
會在每個圖片之間、上方和下方均勻分割可用垂直空間。
MainAxisAlignment
和 CrossAxisAlignment
列舉提供各種常數來控制對齊方式。
Flutter 包含其他可用於對齊的 Widget,尤其是 Align
Widget。
在 Row 和 Column 中調整 Widget 大小
#當版面配置太大而無法容納裝置時,受影響的邊緣會出現黃色和黑色條紋圖案。在此範例中,視窗的寬度為 400 像素,而每個子 Widget 的寬度為 150 像素。
可以使用 Expanded
Widget 來調整 Widget 的大小,使其符合 Row 或 Column。若要修正先前圖片的 Row 對其渲染 Box 來說太寬的範例,請使用 Expanded
Widget 包裹每個圖片。
Expanded
Widget 也可以決定 Widget 相對於其同級 Widget 應佔用多少空間。例如,您可能希望一個 Widget 佔用其同級 Widget 兩倍的空間。為此,請使用 Expanded
Widget 的 flex
屬性,這是一個整數,用於決定 Widget 的彈性係數。預設的彈性係數為 1。以下程式碼將中間圖片的彈性係數設定為 2
DevTools 與除錯版面配置
#在某些情況下,Box 的約束條件是無界的或無限的。這表示最大寬度或最大高度設定為 double.infinity
。當被賦予無界限的約束條件時,試圖盡可能大的 Box 將無法正常運作,並且在除錯模式下會擲回例外狀況。
渲染 Box 最終獲得無界限約束條件的最常見情況是位於彈性 Box (Row
或 Column
) 內,以及可捲動區域內 (例如 ListView
和其他 ScrollView
子類別)。例如,ListView
會嘗試擴展以符合其交叉方向的可用空間 (它可能是一個垂直捲動區塊,並嘗試與其父 Widget 一樣寬)。如果您在水平捲動的 ListView
內巢狀一個垂直捲動的 ListView
,則內部清單會嘗試盡可能寬,這是無限寬,因為外部清單在該方向上是可捲動的。
您在建構 Flutter 應用程式時可能遇到的最常見錯誤是由於錯誤地使用版面配置 Widget 所導致,並且稱為「無界限約束條件」錯誤。
如果您在剛開始建構 Flutter 應用程式時只應準備好應對一種錯誤類型,那就是這個錯誤。
可捲動 Widget
#Flutter 有許多內建的 Widget 會自動捲動,並且還提供各種 Widget,您可以自訂這些 Widget 來建立特定的捲動行為。在本頁面上,您將了解如何使用最常見的 Widget 來使任何頁面可捲動,以及用於建立可捲動清單的 Widget。
ListView
#ListView
是一個類似欄的 widget,當其內容長度超出其渲染框時,會自動提供捲動功能。使用 ListView
最基本的方式與使用 Column
或 Row
非常相似。與 column 或 row 不同的是,ListView
要求其子項佔據交叉軸上的所有可用空間,如下例所示。
當您有未知或非常龐大(或無限)數量的列表項目時,通常會使用 ListView
。在這種情況下,最好使用 ListView.builder
建構函式。 builder 建構函式只會建構目前在螢幕上可見的子項。
在以下範例中,ListView
正在顯示一個待辦事項列表。待辦事項是從儲存庫中獲取的,因此待辦事項的數量是未知的。
自適應版面配置
#由於 Flutter 用於建立行動、平板電腦、桌面和網頁應用程式,您可能需要調整您的應用程式,使其根據螢幕大小或輸入裝置等因素而有不同的行為。這被稱為使應用程式具有適應性和響應性。
在製作適應性版面配置時,最有用的 widget 之一是 LayoutBuilder
widget。LayoutBuilder
是 Flutter 中許多使用「builder」模式的 widget 之一。
建構器模式
#在 Flutter 中,您會發現一些在其名稱或建構函式中使用「builder」一詞的 widget。以下列表並不詳盡
這些不同的「builder」對於解決不同的問題非常有用。例如,ListView.builder
建構函式主要用於延遲渲染列表中的項目,而 Builder
widget 則有助於在深層 widget 程式碼中存取 BuildContext
。
儘管它們的使用案例不同,但這些 builder 的運作方式是統一的。Builder widget 和 builder 建構函式都具有名為 'builder' 的引數(或類似的引數,例如 ListView.builder
中的 itemBuilder
),而 builder 引數始終接受回呼。此回呼是一個建構函式。建構函式是將資料傳遞給父 widget 的回呼,而父 widget 使用這些引數來建構並傳回子 widget。建構函式始終至少傳入一個引數,即建構上下文,並且通常至少傳入另一個引數。
例如,LayoutBuilder
widget 用於根據視窗的大小建立響應式版面配置。 builder 回呼主體會傳入它從其父級接收到的 BoxConstraints
,以及 widget 的 'BuildContext'。使用這些約束,您可以根據可用空間傳回不同的 widget。
LayoutBuilder(Flutter 每週 Widget)
在以下範例中,LayoutBuilder
傳回的 widget 會根據視窗是否小於或等於 600 像素,或是大於 600 像素而改變。
同時,ListView.builder
建構函式上的 itemBuilder
回呼會傳入建構上下文和一個 int
。此回呼會針對列表中的每個項目呼叫一次,而 int 引數代表列表項目的索引。當 Flutter 建構 UI 時,第一次呼叫 itemBuilder 回呼時,傳遞給該函數的 int 是 0,第二次是 1,依此類推。
這允許您根據索引提供特定的配置。回想一下上面使用 ListView.builder
建構函式的範例
final List<ToDo> items = Repository.fetchTodos();
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, idx) {
var item = items[idx];
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(item.description),
Text(item.isComplete),
],
),
);
},
);
}
此範例程式碼使用傳遞到 builder 中的索引,從項目列表中抓取正確的待辦事項,然後在從 builder 傳回的 widget 中顯示該待辦事項的資料。
為了說明這一點,以下範例會變更每隔一個列表項目的背景顏色。
其他資源
#- 常見的版面配置 widget 和概念
- 調整 widget 大小和位置
- 可捲動的 widget
- 範例程式碼:處理長列表
- 範例程式碼:建立水平列表
- 範例程式碼:建立網格列表
- 影片:ListView — Flutter 每週 Widget
- 適應性應用程式
API 參考
#以下資源說明個別 API。
意見回饋
#由於網站的此部分正在發展中,我們歡迎您提供回饋!
除非另有說明,否則本網站上的文件反映了 Flutter 的最新穩定版本。頁面上次更新於 2024-11-18。 檢視原始碼 或 回報問題。