Skip to main content

版面配置

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

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

你將完成的事項

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

Steps

1

簡介

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

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

2

Scaffold 與 AppBar

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

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

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

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

在你的 MainApp 元件的 build 方法中, 為 Scaffold 元件加入 AppBar。 以下程式碼使用了額外的版面配置元件:Align。 這會將標題定位到左側,否則預設會置中。 Text 元件包含標題本身的文字。

直接傳入列舉或靜態屬性(例如 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: const Align(
            alignment: Alignment.centerLeft,
            child: Text('Birdle'),
          ),
        ),
        body: const Center(
          child: Tile('A', HitType.hit),
        ),
      ),
    );
  }
}

更新後的元件樹

#

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

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();
  }
}

接著更新你的 MainApp 元件,改為建立並 顯示 GamePage 元件,而非 Tile 元件。

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

使用 Column 和 Row 排列元件

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

A screenshot that resembles the popular game Wordle.

建置此版面配置有多種方式。 最簡單的是使用 Column 和 Row 元件。 每列包含五個代表猜測中五個字母的方格, 共有五列。 因此你需要一個 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 用於靜態內容。

    Not quite.

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

  2. Column 垂直排列子元件;Row 水平排列子元件。

    That's right!

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

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

    Not quite.

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

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

    Not quite.

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

Scaffold 元件在 Flutter 應用程式中提供什麼功能?
  1. 僅為頁面提供背景顏色。

    Not quite.

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

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

    That's right!

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

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

    Not quite.

    導覽由 Navigator 處理,而非 Scaffold。

  4. 頁面的自動狀態管理。

    Not quite.

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