MDC-103 Flutter:使用顏色、形狀、高度和類型的 Material Design 主題設定

1. 簡介

logo_components_color_2x_web_96dp.png

Material Design 元件 (MDC) 可協助開發人員實作質感設計。MDC 是由 Google 工程師和使用者體驗設計師團隊打造,提供數十種精美且功能豐富的 UI 元件,適用於 Android、iOS、網頁和 Flutter.material.io/develop

你現在可以使用 Material Flutter 自訂應用程式展現獨特風格Material Design 最近的擴展功能為設計人員和開發人員提供更多彈性,以呈現產品的品牌。

在程式碼研究室 (MDC-101MDC-102) 中,您使用了 Material Flutter 來建構名為 Shrine 的應用程式基本資訊,這是一款販售服飾和居家用品的電子商務應用程式。這個應用程式的使用者流程是從登入畫面開始,然後將使用者導向到顯示產品的主畫面。

建構項目

在本程式碼研究室中,您將使用以下項目自訂 Shrine 應用程式:

  • 顏色
  • 字體排版
  • 海拔高度
  • 圖案
  • 版面配置

Android

iOS

主題棕色和粉紅色主題的神社登入頁面

主題棕色和粉紅色主題的神社登入頁面

神社產品頁面,有頂端應用程式列,以及非對稱式、可水平捲動的格狀格線和各種產品 (主題粉紅色)

本程式碼研究室中的 Material Flutter 元件和子系統

  • 主題
  • 字體排版
  • 海拔高度
  • 圖片清單

針對 Flutter 開發經驗,您會給予什麼評價?

新手 中級 還算容易

2. 設定 Flutter 開發環境

您需要使用兩項軟體:Flutter SDK編輯器

您可以使用下列任一裝置執行程式碼研究室:

  • 將實體 AndroidiOS 裝置接上電腦,並設為開發人員模式。
  • iOS 模擬器 (需要安裝 Xcode 工具)。
  • Android Emulator (需要在 Android Studio 中設定)。
  • 瀏覽器 (必須使用 Chrome 進行偵錯)。
  • 下載 WindowsLinuxmacOS 桌面應用程式。您必須在要部署的平台上進行開發。因此,如果您想要開發 Windows 電腦版應用程式,就必須在 Windows 上進行開發,以便存取適當的建構鏈結。如要進一步瞭解作業系統的特定需求,請參閱 docs.flutter.dev/desktop

3. 下載程式碼研究室的範例應用程式

您使用的是 MDC-102 版本嗎?

完成 MDC-102 後,您的程式碼應該就可以用於本程式碼研究室。跳至步驟:變更顏色

還是從頭開始?

下載程式碼研究室入門應用程式

範例應用程式位於 material-components-flutter-codelabs-103-starter_and_102-complete/mdc_100_series 目錄中。

...或是從 GitHub 複製檔案

如要從 GitHub 複製本程式碼研究室,請執行下列指令:

git clone https://github.com/material-components/material-components-flutter-codelabs.git
cd material-components-flutter-codelabs/mdc_100_series
git checkout 103-starter_and_102-complete

開啟專案並執行應用程式

  1. 在您選擇的編輯器中開啟專案。
  2. 按照操作說明「執行應用程式」。

大功告成!您應該會在裝置上看到先前程式碼研究室的 Shrine 登入頁面。

Android

iOS

不同主題的神社登入頁面

不同主題的神社登入頁面

點選「下一步」即可查看產品頁面

Android

iOS

不同主題的神社產品格線頁面

不同主題的神社產品格線頁面

4. 變更色彩

我們製作了代表神社品牌的色彩配置,而設計人員希望您能在整個 Shrine 應用程式中導入這個色彩配置

首先,請將這些顏色匯入專案。

建立 colors.dart

lib 中建立名為 colors.dart 的新 dart 檔案。匯入 material.dart 並新增 const Color 值:

import 'package:flutter/material.dart';

const kShrinePink50 = Color(0xFFFEEAE6);
const kShrinePink100 = Color(0xFFFEDBD0);
const kShrinePink300 = Color(0xFFFBB8AC);
const kShrinePink400 = Color(0xFFEAA4A4);

const kShrineBrown900 = Color(0xFF442B2D);

const kShrineErrorRed = Color(0xFFC5032B);

const kShrineSurfaceWhite = Color(0xFFFFFBFA);
const kShrineBackgroundWhite = Colors.white;

自訂調色盤

這個顏色主題是由設計人員使用自訂顏色建立 (如下圖所示)。當中包含從神社品牌挑選的色彩,並套用至 Material 主題編輯器,這種編輯器的色彩進一步擴大,呈現出更全面的調色盤。(這些顏色並非來自 2014 年 Material 調色盤)。

Material Design 主題編輯器已將這些顏色整理成以數字標示的色調,包括每種顏色標籤 50、100、200、.... 到 900。神社只使用粉色色的 50、100 和 300 層,以及棕色樣本中 900 的陰影。

d0362cb45c565a8e.jpeg 470b0e1c2669ae2.png

小工具的每個彩色參數都會對應到這些配置中的顏色。例如,文字欄位在主動接收輸入內容時,裝飾的顏色應為主題的「Primary」顏色。如果顏色不透明 (很容易在背景上可見),請改用其他顏色。

現在,我們有了要使用的顏色,就可以將這些顏色套用至 UI。方法是設定 ThemeData 小工具的值,並套用至小工具階層頂端的 MaterialApp 例項。

自訂 ThemeData.light()

Flutter 含有一些內建主題。其中之一是淺色主題。我們不會從頭開始製作 ThemeData 小工具,而是會複製淺色主題並變更值,以便為應用程式自訂主題。

讓我們在 app.dart. 中匯入 colors.dart

import 'colors.dart';

接著,將以下內容新增至 ShrineApp 類別的「外部」範圍:

// TODO: Build a Shrine Theme (103)
final ThemeData _kShrineTheme = _buildShrineTheme();

ThemeData _buildShrineTheme() {
  final ThemeData base = ThemeData.light(useMaterial3: true);
  return base.copyWith(
    colorScheme: base.colorScheme.copyWith(
      primary: kShrinePink100,
      onPrimary: kShrineBrown900,
      secondary: kShrineBrown900,
      error: kShrineErrorRed,
    ),
    // TODO: Add the text themes (103)
    // TODO: Decorate the inputs (103)
  );
}

現在,將 ShrineApp build() 函式結尾處的 theme: (在 MaterialApp 小工具中) 設為新的主題:

  // TODO: Customize the theme (103)
  theme: _kShrineTheme, // New code

儲存專案。登入畫面現在應如下所示:

Android

iOS

粉紅色和棕色主題的神社登入頁面

粉紅色和棕色主題的神社登入頁面

5. 修改字體排版和標籤樣式

除了顏色改變之外,設計人員也為使用者提供了特定的字體排版。Flutter 的 ThemeData 包含 3 個文字主題。每個文字主題都是一組文字樣式,例如「廣告標題」和「title」我們會為應用程式採用幾種樣式,並變更部分值。

自訂文字主題

如要將字型匯入專案,必須將這些字型新增至 pubspec.yaml 檔案。

在 pubspec.yaml 中,將以下內容加到 flutter: 標記後方:

  # TODO: Insert Fonts (103)
  fonts:
    - family: Rubik
      fonts:
        - asset: fonts/Rubik-Regular.ttf
        - asset: fonts/Rubik-Medium.ttf
          weight: 500

現在您可以存取及使用 Rubik 字型。

排解 pubspec 檔案問題

如果您剪下並貼上上述宣告,執行 pub get 時可能會發生錯誤。如果發生錯誤,請先移除開頭的空白字元,然後使用 2 個空格縮排來取代該空白字元。(前兩個空格

fonts:

,前面有四個空格

family: Rubik

等。)

如果看到「這裡不允許對應值」,請檢查有問題的行縮排,以及該行上方的縮排。

login.dart 中,在 Column() 內變更以下內容:

Column(
  children: <Widget>[
    Image.asset('assets/diamond.png'),
    const SizedBox(height: 16.0),
    Text(
      'SHRINE',
      style: Theme.of(context).textTheme.headlineSmall,
    ),
  ],
)

app.dart_buildShrineTheme() 後方,新增以下內容:

// TODO: Build a Shrine Text Theme (103)
TextTheme _buildShrineTextTheme(TextTheme base) {
  return base
      .copyWith(
        headlineSmall: base.headlineSmall!.copyWith(
          fontWeight: FontWeight.w500,
        ),
        titleLarge: base.titleLarge!.copyWith(
          fontSize: 18.0,
        ),
        bodySmall: base.bodySmall!.copyWith(
          fontWeight: FontWeight.w400,
          fontSize: 14.0,
        ),
        bodyLarge: base.bodyLarge!.copyWith(
          fontWeight: FontWeight.w500,
          fontSize: 16.0,
        ),
      )
      .apply(
        fontFamily: 'Rubik',
        displayColor: kShrineBrown900,
        bodyColor: kShrineBrown900,
      );
}

這項操作會採用 TextTheme,並變更廣告標題、標題和說明的外觀。

以這種方式套用 fontFamily,只會將變更套用到 copyWith() 中指定的字體排版比例值 (廣告標題、標題、說明文字)。

針對部分字型,我們會設定自訂 fontWeight,以 100 為單位遞增:w500 (500 粗細),對應中 , w400 對應到一般。

使用新的文字內容

發生錯誤後,將下列主題新增至 _buildShrineTheme

// TODO: Add the text themes (103)
textTheme: _buildShrineTextTheme(base.textTheme),
textSelectionTheme: const TextSelectionThemeData(
  selectionColor: kShrinePink100,
),

儲存專案。此外,由於我們修改了字型,這次也請重新啟動應用程式 (也稱為「Hot Restart」)。

Android

iOS

已套用文字主題的神社產品格線頁面

登入畫面和主畫面上的文字看起來不太一樣:有些文字使用 Rubik 字型,而其他文字則是以棕色顯示,而非黑色或白色。圖示也會顯示為棕色。

縮小文字

標籤太大。

home.dart 中,變更最內部資料欄的 children:

// TODO: Change innermost Column (103)
children: <Widget>[
// TODO: Handle overflowing labels (103)
  Text(
    product.name,
    style: theme.textTheme.button,
    softWrap: false,
    overflow: TextOverflow.ellipsis,
    maxLines: 1,
  ),
  const SizedBox(height: 4.0),
  Text(
    formatter.format(product.price),
    style: theme.textTheme.bodySmall,
  ),
  // End new code
],

將文字置中並置於中央

我們希望將標籤置中,並將文字對齊每張資訊卡底部,而非每張圖片的底部。

將標籤移至主軸的結尾 (底部),並將標籤變更為置中:

  // TODO: Align labels to the bottom and center (103)
  mainAxisAlignment: MainAxisAlignment.end,
  crossAxisAlignment: CrossAxisAlignment.center,

儲存專案。

Android

iOS

含有不同文字對齊方式的神社產品方格頁面

含有不同文字對齊方式的神社產品方格頁面

這樣看起來好多了。

設定文字欄位的主題

您也可以使用 InputDecorationTheme 設定文字欄位的裝飾主題。

app.dart_buildShrineTheme() 方法中指定 inputDecorationTheme: 值:

// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
  border: OutlineInputBorder(),
),

目前文字欄位有 filled 裝飾。讓我們將其移除。移除 filled 並指定 inputDecorationTheme,文字欄位就會有外框樣式。

login.dart 中,移除 filled: true 值:

// Remove filled: true values (103)
TextField(
  controller: _usernameController,
  decoration: const InputDecoration(
    // Removed filled: true
    labelText: 'Username',
  ),
),
const SizedBox(height: 12.0),
TextField(
  controller: _passwordController,
  decoration: const InputDecoration(
    // Removed filled: true
    labelText: 'Password',
  ),
  obscureText: true,
),

熱重新啟動。如果 [使用者名稱] 欄位處於有效狀態 (輸入時),您的登入畫面看起來應該會像這樣:

Android

iOS

聚焦在使用者名稱欄位的神社登入頁面

聚焦在使用者名稱欄位的神社登入頁面

在文字欄位中輸入文字 - 邊框和浮動標籤會以主要顏色顯示。然而,這種情況並不容易。無法區分像素色彩對比不足的使用者。(詳情請參閱 Material 指南的顏色與無障礙功能相關文章)。

app.dart 中,在 inputDecorationTheme: 下指定 focusedBorder:

// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
  border: OutlineInputBorder(),
  focusedBorder: OutlineInputBorder(
    borderSide: BorderSide(
      width: 2.0,
      color: kShrineBrown900,
    ),
  ),
),

接著,在 inputDecorationTheme: 下指定 floatingLabelStyle:

// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
  border: OutlineInputBorder(),
  focusedBorder: OutlineInputBorder(
    borderSide: BorderSide(
      width: 2.0,
      color: kShrineBrown900,
    ),
  ),
  floatingLabelStyle: TextStyle(
    color: kShrineBrown900,
  ),
),

最後,將「取消」按鈕設為使用次要顏色,而非主要顏色來提升對比。

TextButton(
  child: const Text('CANCEL'),
  onPressed: () {
    _usernameController.clear();
    _passwordController.clear();
  },
  style: TextButton.styleFrom(
    primary: Theme.of(context).colorScheme.secondary,
  ),
),

儲存專案。

Android

iOS

設有可存取「取消」按鈕的神社登入頁面

設有可存取「取消」按鈕的神社登入頁面

6. 調整高度

您已為頁面設定符合 Shrine 的特定顏色和字體樣式,現在要調整高度。

變更「繼續」按鈕的高度

ElevatedButton 的預設高度為 2。讓我們提高目標。

login.dart 中,將 style: 值新增至 NEXT ElevatedButton:

ElevatedButton(
  child: const Text('NEXT'),
  onPressed: () {
    Navigator.pop(context);
  },
  style: ElevatedButton.styleFrom(
    foregroundColor: kShrineBrown900,
    backgroundColor: kShrinePink100,
    elevation: 8.0,
  ),
),

儲存專案。

Android

iOS

有進階「繼續」按鈕的神社登入頁面

有進階「繼續」按鈕的神社登入頁面

調整資訊卡高度

目前,資訊卡應該位於網站導覽頁面旁邊的白色表面上。

home.dart 中,將 elevation: 值新增至資訊卡:

// TODO: Adjust card heights (103)
elevation: 0.0,

儲存專案。

Android

iOS

每張牌的神社產品方格頁面 (不含海拔高度)

每張牌的神社產品方格頁面 (不含海拔高度)

移除資訊卡下的陰影。

7. 新增形狀

神社擁有酷炫的幾何風格,以八角或長方形設計元素。讓我們在主畫面的資訊卡以及登入畫面上的文字欄位和按鈕,實作該形狀樣式。

變更登入畫面上的文字欄位圖案

app.dart 中,匯入下列檔案:

import 'supplemental/cut_corners_border.dart';

同樣在 app.dart 中,請修改文字欄位裝飾主題,以使用裁剪角落的邊框:

// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
  border: CutCornersBorder(),
  focusedBorder: CutCornersBorder(
    borderSide: BorderSide(
      width: 2.0,
      color: kShrineBrown900,
    ),
  ), 
  floatingLabelStyle: TextStyle(
    color: kShrineBrown900,
  ),
),

變更登入畫面上的按鈕形狀

login.dart 中,為「CANCEL」按鈕加上斜角矩形邊框:

TextButton(
  child: const Text('CANCEL'),
  onPressed: () {
    _usernameController.clear();
    _passwordController.clear();
  },
  style: TextButton.styleFrom(
    foregroundColor: kShrineBrown900,
    shape: const BeveledRectangleBorder(
      borderRadius: BorderRadius.all(Radius.circular(7.0)),
    ),
  ),
),

TextButton 沒有可見的形狀,為什麼要新增邊框形狀?因此,漣漪效果動畫輕觸時會與相同的形狀繫結。

現在,在「Next」按鈕中加入相同的形狀:

ElevatedButton(
  child: const Text('NEXT'),
  onPressed: () {
    Navigator.pop(context);
  },
  style: ElevatedButton.styleFrom(
    foregroundColor: kShrineBrown900,
    backgroundColor: kShrinePink100,
    elevation: 8.0,
    shape: const BeveledRectangleBorder(
      borderRadius: BorderRadius.all(Radius.circular(7.0)),
    ),
  ),
),

如要變更所有按鈕的形狀,也可以在 app.dart 中使用 elevatedButtonThemetextButtonTheme。這已是學員的挑戰!

熱重新啟動。

Android

iOS

套用形狀主題設定的神社登入頁面

套用形狀主題設定的神社登入頁面

8. 變更版面配置

接下來,我們要變更版面配置,以不同的顯示比例和大小顯示資訊卡,讓每張資訊卡看起來與其他資訊卡不同。

將 GridView 替換為 AsymmetricView

我們已經為非對稱版面配置編寫檔案。

home.dart 中新增以下匯入項目:

import 'supplemental/asymmetric_view.dart';

刪除 _buildGridCards 並替換 body

body: AsymmetricView(
  products: ProductsRepository.loadProducts(Category.all),
),

儲存專案。

Android

iOS

象徵性可水平捲動版面配置的神社產品頁面

象徵性可水平捲動版面配置的神社產品頁面

現在,產品會以編織紋風格水平捲動。

9. 試試其他主題 (選用)

顏色是展現品牌精神的有效方法,稍微改變顏色則可提升使用者體驗。如要測試這項功能,我們來看看當品牌的色彩配置略有不同時,神殿的樣子。

修改顏色

colors.dart 中新增下列顏色:

const kShrinePurple = Color(0xFF5D1049);

app.dart 中,將 _buildShrineTheme() 函式變更為以下內容:

ThemeData _buildShrineTheme() {
  final ThemeData base = ThemeData.light();
  return base.copyWith(
    colorScheme: base.colorScheme.copyWith(
      primary: kShrinePurple,
      secondary: kShrinePurple,
      error: kShrineErrorRed,
    ),
    scaffoldBackgroundColor: kShrineSurfaceWhite,
    textSelectionTheme: const TextSelectionThemeData(
      selectionColor: kShrinePurple,
    ),
    appBarTheme: const AppBarTheme(
      foregroundColor: kShrineBrown900,
      backgroundColor: kShrinePink100,
    ),

    inputDecorationTheme: const InputDecorationTheme(
      border: CutCornersBorder(),
      focusedBorder: CutCornersBorder(
        borderSide: BorderSide(
          width: 2.0,
          color: kShrinePurple,
        ),
      ),
      floatingLabelStyle: TextStyle(
        color: kShrinePurple,
      ),
    ),
  );
}

熱重新啟動。這時應該會顯示新主題。

Android

iOS

紫色和粉紅色主題的神社登入頁面

紫色和粉紅色主題的神社登入頁面

Android

iOS

粉紅色主題的神社產品頁面

粉紅色主題的神社產品頁面

結果非常不同!讓我們把 app.dart's _buildShrineTheme 還原為這個步驟之前的項目。或下載 104 的範例程式碼。

10. 恭喜!

您現在已建立與設計人員設計規格類似的應用程式。

後續步驟

您已使用下列 Material Flutter:主題、字體排版、高度和形狀。您可以在 Material Flutter 程式庫中探索更多元件和子系統。

深入瞭解 supplemental 目錄中的檔案,瞭解我們如何水平捲動非對稱版面配置格線。

如果您規劃的應用程式設計含有沒有程式庫元件的元素,該怎麼辦?在 MDC-104:Material 進階元件中,我們示範如何使用 Material Flutter 程式庫建立自訂元件,達到想要的外觀。

我可以在合理的時間內,完成本程式碼研究室

非常同意 同意 普通 不同意 非常不同意

我想日後繼續使用 Material Design 元件

非常同意 同意 普通 不同意 非常不同意