Skip to main content

版面配置

了解 Flutter 中常見的版面配置元件。

了解如何使用 Scaffold、AppBar、Column 及 Row 等常見元件 (Widget) 建置版面配置。

你將完成的事項

使用 Scaffold 和 AppBar 建構應用程式結構
使用 Column 和 Row 排列元件
從資料動態產生元件
為遊戲棋盤建置格狀版面配置

Steps

1

簡介

由於 Flutter 是一套 UI 工具包, 你會花大量時間使用 Flutter 元件建置版面配置。

在本章節中,你將學習如何使用 一些最常見的版面配置元件建置版面配置。 這包含高階元件,例如 ScaffoldAppBar,它們負責畫面的結構排版, 以及低階元件,例如 ColumnRow, 可將元件垂直或水平排列。

2

ScaffoldAppBar

行動應用程式通常在頂端有一條稱為「應用程式列 (app bar)」的橫列, 可顯示標題、導覽控制項及/或操作按鈕。

A screenshot of a simple application with a bar across the top that has a title and settings button.

在應用程式中新增應用程式列最簡單的方式, 是使用兩個元件:ScaffoldAppBar

Scaffold 是一個便利元件,提供 Material 風格的頁面版面配置, 讓你可以輕鬆地在應用程式頁面中加入應用程式列、抽屜式導覽、 導覽列等。AppBar 當然就是應用程式列本身。

flutter create --empty 指令產生的程式碼已包含 AppBar 元件和 Scaffold 元件。 以下程式碼將其更新為使用額外的版面配置元件:Align。 這會將標題定位到左側,否則預設會置中。 Text 元件包含標題本身的文字。

在你的 MainApp 元件的 build 方法中修改 Scaffold

直接傳入列舉或靜態屬性(例如 Alignment.centerLeft) 也可以使用 Dart 的點號簡寫 語法縮短, 你可以在 Dart 官方文件及 Flutter 簡寫概覽 中進一步了解。

dart
class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Align(
            alignment: Alignment.centerLeft,
            child: Text('Birdle'),
          ),
        ),
        body: Center(child: Text('Hello World!')),
      ),
    );
  }
}

更新後的元件樹

#

隨著應用程式成長,考量元件樹的結構變得越來越重要。 在此階段,元件樹首次出現了「分支」, 現在的結構如下圖所示:

A screenshot that resembles the popular game Wordle.
3

為遊戲頁面建立版面配置元件

在你的 main.dart 檔案中加入以下程式碼, 新增一個名為 GamePage 的元件。 此元件最終將顯示遊戲本身所需的 UI 元素。

lib/main.dart
dart
class GamePage extends StatelessWidget {
  GamePage({super.key});
  // This object is part of the game.dart file.
  // It manages wordle logic, and is outside the scope of this tutorial.
  final Game _game = Game();

  @override
  Widget build(BuildContext context) {
    // TODO: Replace with screen contents
    return Container();
  }
}

接著更新你的 MainPage 元件,改為建立並 顯示 GamePage 元件,而非「Hello World!」。

dart
class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Align(
            alignment: Alignment.centerLeft,
            child: Text('Birdle'),
          ),
        ),
        body: Center(child: GamePage()),
      ),
    );
  }
}
4

使用 ColumnRow 排列元件

GamePage 的版面配置包含顯示使用者猜測結果的方格。

A screenshot that resembles the popular game Wordle.

建置此版面配置有多種方式。 最簡單的是使用 ColumnRow 元件。 每列包含五個代表猜測中五個字母的方格, 共有五列。 因此你需要一個 Column,其中包含五個 Row 元件作為子元件, 而每列各包含五個子元件。

首先,將 GamePage.build 中的 Container 替換為 一個以 Column 元件為子元件的 Padding 元件:

dart
class GamePage extends StatelessWidget {
  GamePage({super.key});
  // This manages game logic, and is out of scope for this lesson.
  final Game _game = Game();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        spacing: 5.0,
        children: [
          // Add children next.
        ],
      ),
    );
  }
}

spacing 屬性會在主軸上的每個元素之間加入五個像素的間距。

Column.children 中,針對 _game.guesses 清單中的每個元素, 新增一個 Row 元件作為子元件。

dart
class GamePage extends StatelessWidget {
  GamePage({super.key});
  // This manages game logic, and is out of scope for this lesson.
  final Game _game = Game();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        spacing: 5.0,
        children: [
          for (final guess in _game.guesses)
            Row(
              spacing: 5.0,
              children: [
                // We'll add the tiles here later.
              ],
            ),
        ],
      ),
    );
  }
}

children 清單中的 for 迴圈稱為集合 for 元素 (collection for element), 這是一種 Dart 語法,可讓你在集合於執行時期建置時 以迭代方式加入項目。 這種語法糖讓你更輕鬆地處理 元件集合,提供了以下寫法的宣告式替代方案:

dart
[..._game.guesses.map((guess) => Row(/* ... */))],

在此範例中,它會為 Game 物件上的每個猜測, 向欄中新增五個 Row 元件。

更新後的元件樹

#

本課程中,應用程式的元件樹大幅擴展。 現在,其結構更像以下(簡化後的)圖示:

A diagram showing a tree like structure with a node for each widget in the app.

重新載入應用程式後,你應該會看到一個 5x5 的白色方格陣列。

A screenshot that resembles the popular game Wordle.
5

回顧

你完成的事項

以下是本課程中你所建置及學習內容的摘要。
使用 Scaffold 和 AppBar 建構應用程式結構

你使用 Scaffold 提供 Material 風格的頁面版面配置,並使用 AppBar 在應用程式頂端加入標題列。 這些高階元件為你的應用程式提供了標準且精緻的結構。

使用 Column 和 Row 排列元件

Column 垂直排列元件,Row 水平排列元件。 這些是你在 Flutter 開發中會不斷使用的基本版面配置元件。 spacing 屬性可在子元件之間加入一致的間距。

從資料動態產生元件

你使用集合 for 元素從清單建置元件。 這種宣告式方法讓你能建置自動且直觀地 反映資料的使用者介面, 這是 Flutter 開發的核心模式。

建置遊戲棋盤格狀版面配置

透過在 Column 中巢狀嵌入 Row 元件並使用巢狀迴圈, 你建立了一個 5x5 的 Tile 元件格狀版面。 你的應用程式現在已顯示完整的遊戲棋盤版面配置!

6

自我測驗

版面配置小測驗

1 / 2
Column 和 Row 元件的主要差異是什麼?
  1. Column 垂直排列子元件;Row 水平排列子元件。

    That's right!

    Column 沿垂直軸排列子元件,而 Row 則使用水平軸。

  2. Column 用於可捲動內容;Row 用於靜態內容。

    Not quite

    Column 和 Row 都用於版面配置,而非捲動。捲動請使用 ListView 或 SingleChildScrollView。

  3. Column 需要 Scaffold 作為父元件;Row 則不需要。

    Not quite

    這兩個元件都不需要以 Scaffold 作為父元件。

  4. Column 可有無限個子元件;Row 限制為兩個。

    Not quite

    兩個元件都可以有任意數量的子元件。

Scaffold 元件在 Flutter 應用程式中提供什麼功能?
  1. 頁面的自動狀態管理。

    Not quite

    Scaffold 不管理狀態;你需要使用 StatefulWidget 或狀態管理解決方案來處理這個問題。

  2. 一個 Material 風格的頁面版面配置,包含應用程式列、主體、抽屜式導覽等插槽。

    That's right!

    Scaffold 是一個便利元件,提供標準的 Material 頁面結構。

  3. 一種在不同頁面之間導覽的方式。

    Not quite

    導覽由 Navigator 處理,而非 Scaffold。

  4. 僅為頁面提供背景顏色。

    Not quite

    Scaffold 提供的功能遠不止這些,包含應用程式列、抽屜式導覽等結構。