Flutter 中的版面配置
了解 Flutter 的版面配置機制如何運作,以及如何建立應用程式的版面配置。
概覽
#Flutter 版面配置機制的核心就是元件 (Widget)。 在 Flutter 中,幾乎所有東西都是元件—甚至 版面配置模型本身也是元件。你在 Flutter 應用程式中看到的 圖片、圖示和文字,都是元件。 但你看不到的東西也是元件, 像是用來排列、限制和對齊可見元件的 行(row)、列(column)和網格(grid)。 你可以透過組合元件來建立更複雜的元件, 進而完成版面配置。
概念範例
#
在以下範例中,第一張螢幕截圖顯示了三個帶有標籤的圖示,
而第二張螢幕截圖則包含了行與列的視覺化版面配置。
在第二張螢幕截圖中,debugPaintSizeEnabled 被設為 true,
讓你可以看到版面配置的視覺化效果。
以下是前述範例的元件樹(widget tree)圖示:
大部分內容應該如你所預期,但你可能會對那些(粉紅色顯示的)容器感到好奇。Container
是一個元件類別,
它允許你自訂其子元件(child widget)。當你想要為子元件加入
內距(padding)、外距(margin)、邊框(border)或背景顏色等效果時,
就可以使用 Container。
每個 Text 元件都被放在 Container
中,以加入外距。整個 Row
也被放在
Container 中,以在該行周圍加入內距。
其餘的 UI 則由屬性控制。
你可以透過 Icon
的 color 屬性來設定顏色。
使用 Text.style 屬性可以設定字型、顏色、字重等。
列(columns)與行(rows)則有屬性可讓你指定其子元件
如何垂直或水平對齊,以及子元件應該佔用多少空間。
排版單一元件
#在 Flutter 中,如何排版一個單一元件? 本節將示範如何建立並顯示一個簡單的元件, 同時也會展示一個簡單 Hello World 應用程式的完整程式碼。
在 Flutter 中,只需幾個步驟即可將文字、圖示或圖片顯示在螢幕上。
1. 選擇一個版面配置元件
#你可以根據想要如何對齊或限制可見元件, 從多種版面配置元件中選擇, 因為這些特性通常會傳遞給其所包含的元件。
例如,你可以使用
Center
版面配置元件,
將可見元件在水平與垂直方向上置中顯示:
Center(
// Content to be centered here.
)
2. 建立可見元件
#為你的應用程式選擇一個可見元件來承載可見元素,例如文字、圖片或 圖示。
例如,你可以使用 Text 元件顯示一些文字:
Text('Hello World')
3. 將可見元件加入版面配置元件
#所有版面配置元件都有以下其中之一:
-
如果只接收單一子元件,則有
child屬性,例如Center或Container -
如果接收元件清單,則有
children屬性,例如Row、Column、ListView或Stack。
將 Text 元件加入 Center 元件中:
const Center(
child: Text('Hello World'),
),
4. 將版面配置元件加入頁面
#
一個 Flutter 應用程式本身就是一個元件,而大多數元件都有一個 build()
方法。在應用程式的 build() 方法中實例化並回傳一個元件,便會顯示該元件。
對於一般應用程式,你可以將 Container 元件加入應用程式的 build() 方法中:
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(color: Colors.white),
child: const Center(
child: Text(
'Hello World',
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 32, color: Colors.black87),
),
),
);
}
}
預設情況下,一般應用程式不會包含 AppBar、標題或背景顏色。如果你希望在一般應用程式中加入這些功能,則需要自行實作。本範例應用程式將背景顏色設為白色,文字設為深灰色,以模仿 Material 應用程式的風格。
對於 Material 應用程式,你可以使用 Scaffold
元件;它會提供預設橫幅、背景顏色,並且有 API 可用於新增抽屜(drawer)、訊息條(snack bar)以及底部彈窗(bottom sheet)。接著,你可以將 Center
元件直接加到首頁的 body 屬性中。
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
const String appTitle = 'Flutter layout demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(title: const Text(appTitle)),
body: const Center(
child: Text('Hello World'),
),
),
);
}
}
若要建立 Cupertino 應用程式,
請使用 CupertinoApp 與 CupertinoPageScaffold
元件。
與 Material 不同,這裡不會自動提供預設的橫幅或背景顏色。
你需要自行設定。
-
若要設定預設顏色,請將已設定的
CupertinoThemeData傳入應用程式的theme屬性。 -
若要在應用程式頂部加入 iOS 風格的導覽列,請在 scaffold 的
navigationBar屬性中加入CupertinoNavigationBar元件。 你可以使用CupertinoColors提供的顏色來 設定元件,使其符合 iOS 設計風格。 -
若要配置應用程式的主體內容,請將 scaffold 的
child屬性 設為你想要的元件,例如Center或Column。
想了解還有哪些 UI 元件可以加入,請參考 Cupertino 函式庫。
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter layout demo',
theme: CupertinoThemeData(
brightness: Brightness.light,
primaryColor: CupertinoColors.systemBlue,
),
home: CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
backgroundColor: CupertinoColors.systemGrey,
middle: Text('Flutter layout demo'),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text('Hello World')],
),
),
),
);
}
}
5. 執行你的應用程式
#
垂直與水平排列多個元件
#
最常見的版面配置模式之一,就是將元件
垂直或水平排列。你可以使用
Row 元件來水平排列元件,
使用 Column 元件來垂直排列元件。
要在 Flutter 中建立 row 或 column,只需將一個子元件清單
加入 Row
或 Column 元件。每個子元件本身
也可以是 row 或 column,如此類推。
以下範例展示了如何在 row 或 column 內巢狀放置 row 或 column。
這個版面配置以 Row 為主體。該 row 包含兩個子元件:
左側是一個 column,右側是一張圖片:
左側 column 的元件樹中巢狀了多個 row 與 column。
你將在巢狀 row 與 column中實作 Pavlova 的部分版面配置程式碼。
對齊元件
#
你可以透過 mainAxisAlignment 與 crossAxisAlignment 屬性來控制 row 或 column 如何對齊其子元件。
對於 row,主軸(main axis)為水平方向,交叉軸(cross axis)為垂直方向。
對於 column,主軸為垂直方向,交叉軸為水平方向。
MainAxisAlignment
與 CrossAxisAlignment
列舉型別(enum)提供多種常數,可用來控制對齊方式。
在下列範例中,每張圖片寬度皆為 100 像素。
渲染區域(本例為整個螢幕)寬度超過 300 像素,因此將主軸對齊設為 spaceEvenly
會讓多餘的水平空間平均分配在每張圖片的前後與之間。
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset('images/pic1.jpg'),
Image.asset('images/pic2.jpg'),
Image.asset('images/pic3.jpg'),
],
);

App source: row_column
Column(欄)與 Row(列)的運作方式相同。以下範例展示了一個包含 3 張圖片的 column,每張圖片高度為 100 像素。渲染區域(本例為整個螢幕)高度大於 300 像素,因此將主軸對齊設為
spaceEvenly 時,會將多餘的垂直空間平均分配在每張圖片之間、上方與下方。
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset('images/pic1.jpg'),
Image.asset('images/pic2.jpg'),
Image.asset('images/pic3.jpg'),
],
);

App source: row_column
元件尺寸調整
#當版面配置過大,無法完全顯示於裝置螢幕時,受影響的邊緣會出現黃黑相間的條紋圖案。 以下是一個 row 過寬的範例:
可以透過使用 Expanded
元件,讓元件尺寸適合在 row 或 column 內顯示。若要修正前述圖片 row 超出其渲染區域的情況,
請將每個圖片包裹在 Expanded 元件中。
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(child: Image.asset('images/pic1.jpg')),
Expanded(child: Image.asset('images/pic2.jpg')),
Expanded(child: Image.asset('images/pic3.jpg')),
],
);

App source: sizing
有時你可能希望某個元件佔據比其同列元件多兩倍的空間。此時,可以使用 Expanded 元件的 flex 屬性,這是一個整數,用來決定該元件的彈性係數(flex factor)。預設的彈性係數為 1。以下程式碼將中間圖片的彈性係數設為 2:
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(child: Image.asset('images/pic1.jpg')),
Expanded(flex: 2, child: Image.asset('images/pic2.jpg')),
Expanded(child: Image.asset('images/pic3.jpg')),
],
);

App source: sizing
元件緊密排列
#
預設情況下,row 或 column 會沿著其主軸盡可能佔據空間,但如果你希望讓子元件緊密排列,可以將其 mainAxisSize 設為 MainAxisSize.min。以下範例使用此屬性,將星形圖示元件緊密排列在一起。
Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
const Icon(Icons.star, color: Colors.black),
const Icon(Icons.star, color: Colors.black),
],
)

App source: pavlova
巢狀的 row 與 column {#nesting-rows-and-columns}
#版面配置框架允許你在 row 與 column 之中,依需求任意深度地巢狀 row 與 column。 讓我們來看看下方版面配置中紅框標示區塊的程式碼:
紅框標示的區塊是由兩個 row 實作而成。評分 row 包含五顆星星與評論數量。圖示 row 則包含三個由圖示與文字組成的 column。
評分 row 的元件樹如下:
ratings 變數建立了一個 row,其中包含一個較小的 5 星圖示 row,以及文字:
final stars = Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
const Icon(Icons.star, color: Colors.black),
const Icon(Icons.star, color: Colors.black),
],
);
final ratings = Container(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
stars,
const Text(
'170 Reviews',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w800,
fontFamily: 'Roboto',
letterSpacing: 0.5,
fontSize: 20,
),
),
],
),
);
在評分列(ratings row)下方的圖示列(icons row)包含 3 個欄位,每個欄位內都有一個圖示和兩行文字,如下圖的元件樹所示:
iconList 變數定義了圖示列:
const descTextStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w800,
fontFamily: 'Roboto',
letterSpacing: 0.5,
fontSize: 18,
height: 2,
);
// DefaultTextStyle.merge() allows you to create a default text
// style that is inherited by its child and all subsequent children.
final iconList = DefaultTextStyle.merge(
style: descTextStyle,
child: Container(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
Icon(Icons.kitchen, color: Colors.green[500]),
const Text('PREP:'),
const Text('25 min'),
],
),
Column(
children: [
Icon(Icons.timer, color: Colors.green[500]),
const Text('COOK:'),
const Text('1 hr'),
],
),
Column(
children: [
Icon(Icons.restaurant, color: Colors.green[500]),
const Text('FEEDS:'),
const Text('4-6'),
],
),
],
),
),
);
leftColumn 變數包含評分與圖示列,以及描述 Pavlova 的標題和文字:
final leftColumn = Container(
padding: const EdgeInsets.fromLTRB(20, 30, 20, 20),
child: Column(children: [titleText, subTitle, ratings, iconList]),
);
左側欄位被放置在 SizedBox 中,以限制其寬度。
最後,整個 UI 是將整行(包含左側欄位和圖片)放在 Card 內建構而成。
Pavlova 圖片 來自 Pixabay。
你可以使用 Image.network() 從網路嵌入圖片,但在本範例中,圖片被儲存於專案的 images 目錄,並新增至 pubspec file,然後透過
Images.asset() 來存取。
如需更多資訊,請參閱新增資源與圖片。
body: Center(
child: Container(
margin: const EdgeInsets.fromLTRB(0, 40, 0, 30),
height: 600,
child: Card(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(width: 440, child: leftColumn),
mainImage,
],
),
),
),
),
App source: pavlova
常用版面配置元件 {#common-layout-widgets}
#Flutter 擁有豐富的版面配置元件函式庫。 以下介紹幾個最常用的元件,目的是讓你能快速上手, 而不是給你一份完整清單。 若需查詢其他可用元件,請參考 Widget catalog, 或在 API 參考文件 的搜尋框中查找。 此外,API 文件中的元件頁面通常也會建議其他可能更適合你需求的相似元件。
以下元件分為兩類:來自 widgets library 的標準元件,以及來自 Material library 的專用元件。任何應用程式都可以使用 widgets library,但 只有 Material 應用程式可以使用 Material Components library。
-
CupertinoPageScaffold 提供 iOS 風格頁面的基本版面結構。
-
CupertinoNavigationBar 在螢幕頂部建立 iOS 風格的導覽列。
-
CupertinoSegmentedControl 建立 iOS 風格的分段控制元件,用於選擇。
-
CupertinoTabBar和CupertinoTabScaffold 建立 iOS 典型的底部分頁列。
Container
#
許多版面配置會大量使用 Container
來透過內距(padding)分隔元件,或是增加邊框與外距(margin)。你可以將整個版面包在一個 Container 中,並更改其背景顏色或圖片,以改變裝置背景。
摘要(Container)
#- 增加內距(padding)、外距(margin)、邊框
- 更改背景顏色或圖片
- 僅包含一個子元件,但該子元件可以是
Row、Column,甚至是元件樹的根節點
範例(Container)
#
此版面由一個包含兩列的 column 組成,每一列各有兩張圖片。利用 Container
將 column 的背景色設為較淺的灰色。
Widget _buildImageColumn() {
return Container(
decoration: const BoxDecoration(color: Colors.black26),
child: Column(children: [_buildImageRow(1), _buildImageRow(3)]),
);
}
Container 也用來為每張圖片新增圓角邊框和外距:
Widget _buildDecoratedImage(int imageIndex) => Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(width: 10, color: Colors.black38),
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
margin: const EdgeInsets.all(4),
child: Image.asset('images/pic$imageIndex.jpg'),
),
);
Widget _buildImageRow(int imageIndex) => Row(
children: [
_buildDecoratedImage(imageIndex),
_buildDecoratedImage(imageIndex + 1),
],
);
你可以在教學課程中找到更多 Container 範例。
App source: container
GridView
#
使用 GridView 將元件以二維清單方式排列。GridView
提供兩種預設的清單,
或是你也可以自訂自己的格線。當 GridView
偵測到內容過長無法完全顯示於渲染區域時,會自動產生捲動功能。
摘要(GridView)
#- 以格線方式排列元件
- 偵測當欄內容超出渲染區域時,自動提供捲動功能
-
你可以自訂自己的格線,或使用以下其中一種預設格線:
GridView.count可讓你指定欄位數量GridView.extent可讓你指定每個格子(tile)的最大像素寬度
範例(GridView)
#
使用 GridView.count 建立一個在直向模式下有 2 欄、橫向模式下有 3 欄的格線。標題是透過為每個 GridTile 設定 footer 屬性產生。
Dart code:
grid_list_demo.dart
Widget _buildGrid() => GridView.extent(
maxCrossAxisExtent: 150,
padding: const EdgeInsets.all(4),
mainAxisSpacing: 4,
crossAxisSpacing: 4,
children: _buildGridTileList(30),
);
// The images are saved with names pic0.jpg, pic1.jpg...pic29.jpg.
// The List.generate() constructor allows an easy way to create
// a list when objects have a predictable naming pattern.
List<Widget> _buildGridTileList(int count) =>
List.generate(count, (i) => Image.asset('images/pic$i.jpg'));
ListView
#
ListView 是一個類似直欄的元件,當內容超出其渲染區域時,
會自動提供捲動功能。
摘要(ListView)
#-
一個專門用於組織方塊清單的
Column - 可以橫向或縱向排列
- 能偵測內容無法容納時自動提供捲動
- 可設定性較
Column低,但更易於使用且支援捲動
範例(ListView)
#Widget _buildList() {
return ListView(
children: [
_tile('CineArts at the Empire', '85 W Portal Ave', Icons.theaters),
_tile('The Castro Theater', '429 Castro St', Icons.theaters),
_tile('Alamo Drafthouse Cinema', '2550 Mission St', Icons.theaters),
_tile('Roxie Theater', '3117 16th St', Icons.theaters),
_tile(
'United Artists Stonestown Twin',
'501 Buckingham Way',
Icons.theaters,
),
_tile('AMC Metreon 16', '135 4th St #3000', Icons.theaters),
const Divider(),
_tile('K\'s Kitchen', '757 Monterey Blvd', Icons.restaurant),
_tile('Emmy\'s Restaurant', '1923 Ocean Ave', Icons.restaurant),
_tile('Chaiya Thai Restaurant', '272 Claremont Blvd', Icons.restaurant),
_tile('La Ciccia', '291 30th St', Icons.restaurant),
],
);
}
ListTile _tile(String title, String subtitle, IconData icon) {
return ListTile(
title: Text(
title,
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
),
subtitle: Text(subtitle),
leading: Icon(icon, color: Colors.blue[500]),
);
}
Stack
#
使用 Stack 來將元件堆疊在一個基礎元件之上—通常是一張圖片。這些元件可以完全
或部分地重疊在基礎元件上。
摘要(Stack)
#- 適用於需要重疊在其他元件上的元件
-
children清單中的第一個元件是基礎元件; 後續的子元件會疊加在該基礎元件之上 Stack的內容無法捲動- 你可以選擇裁剪超出渲染區域的子元件
範例(Stack)
#
使用 Stack 疊加一個 Container(其會在半透明黑色背景上顯示其 Text)於 CircleAvatar 之上。Stack 透過 alignment 屬性與
Alignment 來偏移文字。
App source: card_and_stack
Widget _buildStack() {
return Stack(
alignment: const Alignment(0.6, 0.6),
children: [
const CircleAvatar(
backgroundImage: AssetImage('images/pic.jpg'),
radius: 100,
),
Container(
decoration: const BoxDecoration(color: Colors.black45),
child: const Text(
'Mia B',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
],
);
}
Card
#
一個 Card,來自 Material library,
用於承載相關的資訊片段,可以由幾乎任何元件組成,但通常會與
ListTile
一起使用。Card 僅接受一個子元件,
但其子元件可以是 column、row、list、grid,
或其他支援多個子元件的元件。
預設情況下,Card 會將自身尺寸縮小為 0 x 0 像素。
你可以使用 SizedBox
來限制 card 的尺寸。
在 Flutter 中,Card 具有略為圓角的邊緣
以及投影陰影,帶來 3D 效果。
變更 Card 的 elevation 屬性可以控制
投影陰影的效果。例如,將 elevation 設為 24,
會讓 Card 視覺上從表面抬升得更高,且陰影會更加分散。
支援的 elevation 值請參考 Elevation
於
Material guidelines。
若指定不支援的值,則會完全關閉投影陰影。
摘要(Card)
#- 實作 Material card
- 用於呈現相關的資訊片段
-
僅接受一個子元件,但該子元件可以是
Row、Column,或其他可包含多個子元件的元件 - 以圓角與投影陰影顯示
Card的內容無法捲動- 來自 Material library
範例(Card)
#
一個 Card,內含 3 個 ListTile,並透過包裹
SizedBox 來設定尺寸。Divider 用於分隔
第一個與第二個 ListTiles。
App source: card_and_stack
Widget _buildCard() {
return SizedBox(
height: 210,
child: Card(
child: Column(
children: [
ListTile(
title: const Text(
'1625 Main Street',
style: TextStyle(fontWeight: FontWeight.w500),
),
subtitle: const Text('My City, CA 99984'),
leading: Icon(Icons.restaurant_menu, color: Colors.blue[500]),
),
const Divider(),
ListTile(
title: const Text(
'(408) 555-1212',
style: TextStyle(fontWeight: FontWeight.w500),
),
leading: Icon(Icons.contact_phone, color: Colors.blue[500]),
),
ListTile(
title: const Text('costa@example.com'),
leading: Icon(Icons.contact_mail, color: Colors.blue[500]),
),
],
),
),
);
}
ListTile
#
使用 ListTile,這是一個來自
Material library 的專用 row 元件,可以輕鬆建立一個 row,內含最多 3 行文字,並可選擇性加入前置(leading)及後置(trailing)圖示。ListTile
最常用於
Card
或 ListView,但也可用於其他地方。
摘要(ListTile)
#- 一個專用的 row,可包含最多 3 行文字及可選的圖示
- 可設定性比
Row少,但更容易使用 - 來自 Material library
範例(ListTile)
#限制條件 {#constraints}
#若要完全理解 Flutter 的版面配置系統,你需要 學習 Flutter 如何在版面中定位與調整元件的大小。更多資訊請參閱理解限制條件。
影片
#
以下影片為
Flutter in Focus
系列的一部分,
說明 Stateless 與 Stateful 元件。
每一集 Widget of the Week series 都會聚焦介紹一個元件, 其中有多集包含版面配置元件。
Flutter Widget of the Week 播放清單
其他資源
#以下資源在撰寫版面配置程式碼時可能有所幫助。
- Layout tutorial
學習如何建立版面配置。
- Widget catalog
介紹 Flutter 提供的多種元件。
- HTML/CSS Analogs in Flutter
-
若你熟悉網頁程式設計, 本頁將 HTML/CSS 的功能對應到 Flutter 的特性。
- API reference docs
提供所有 Flutter 函式庫的參考文件。
- Adding assets and images
說明如何將圖片及其他資源加入你的應用程式套件中。
- Zero to One with Flutter
一位開發者撰寫其第一個 Flutter 應用程式的經驗分享。
Unless stated otherwise, the documentation on this site reflects Flutter 3.44.0. Page last updated on 2026-06-14. View source or report an issue.





