MDC-103 Flutter: カラー、シェイプ、高度、タイプによるマテリアルのテーマ設定

1. はじめに

logo_components_color_2x_web_96dp.png

マテリアル コンポーネント(MDC)は、デベロッパーがマテリアル デザインを実装する際に役立ちます。Google のエンジニアと UX デザイナーのチームが作成した MDC には、美しく機能的な UI コンポーネントが多数含まれており、Android、iOS、ウェブ、Flutter.material.io/develop に利用可能です。

MDC を使用すると、アプリのスタイルをこれまで以上に独自のものにカスタマイズできます。マテリアル デザインの最新の拡張では、デザイナーとデベロッパーが商品のブランドを表現するための柔軟性が向上しています。

Codelab MDC-101 および MDC-102 では、マテリアル コンポーネント(MDC)を使用して、Shrine というアプリの土台を構築しました。Shrine は、衣服と生活雑貨を販売する e コマースアプリです。このアプリのユーザーフローは、最初にログイン画面を表示し、次に商品を表示するホーム画面に移動します。

作成するアプリの概要

この Codelab では、以下を使用して Shrine アプリをカスタマイズします。

  • カラー
  • タイポグラフィ
  • 高度
  • シェイプ
  • レイアウト

Android

iOS

茶色とピンクのテーマが設定されている Shrine ログインページ

茶色とピンクのテーマが設定されている Shrine ログインページ

Shrine 商品ページ。上部にアプリバーがあり、横方向にスクロール可能な非対称のグリッドに商品が表示されています。また、ピンクと茶色のテーマが設定されています。

Shrine 商品ページ。上部にアプリバーがあり、横方向にスクロール可能な非対称のグリッドに商品が表示されています。また、ピンクと茶色のテーマが設定されています。

この Codelab の MDC-Flutter コンポーネントとサブシステム

  • テーマ
  • タイポグラフィ
  • 高度
  • 画像リスト

Flutter 開発の経験についてお答えください。

初心者 中級者 上級者

2. Flutter の開発環境をセットアップする

このラボを完了するには、Flutter SDKエディタの 2 つのソフトウェアが必要です。

この Codelab は、次のいずれかのデバイスを使って実行できます。

  • パソコンに接続され、デベロッパー モードに設定された物理デバイス(Android または iOS
  • iOS シミュレータ(Xcode ツールのインストールが必要)
  • Android Emulator(Android Studio でセットアップが必要)
  • ブラウザ(デバッグには Chrome が必要)
  • WindowsLinuxmacOS のデスクトップ アプリケーション。開発はデプロイする予定のプラットフォームで行う必要があります。たとえば、Windows のデスクトップ アプリを開発する場合は、適切なビルドチェーンにアクセスできるように Windows で開発する必要があります。オペレーティング システム固有の要件については、docs.flutter.dev/desktop に詳しい説明があります。

3. Codelab のスターター アプリをダウンロードする

MDC-102 から続行する場合

MDC-102 が完了済みであれば、コードはこの Codelab を進められる状態になっています。「色を変更する」ステップに進んでください。

ゼロから始める

Codelab のスターター アプリをダウンロードする

スターター アプリは material-components-flutter-codelabs-103-starter_and_102-complete/mdc_100_series ディレクトリにあります。

GitHub からクローンを作成する

GitHub からこの Codelab のクローンを作成するには、次のコマンドを実行します。

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. エディタで Get Started: Test drive を開き、「Run the app」セクションの指示に従います。

完了しました。前の Codelab で作成した Shrine のログインページがデバイスに表示されます。

Android

iOS

テーマが設定されていない Shrine ログインページ

テーマが設定されていない Shrine ログインページ

[NEXT] をクリックすると、前の Codelab のホームページが表示されます。

Android

iOS

テーマが設定されていない Shrine 商品グリッドページ

テーマが設定されていない Shrine 商品グリッドページ

4. 色を変更する

Shrine のブランドを表すカラーパターンは作成済みです。デザイナーは、Shrine アプリ全体にそのカラーパターンを実装したいと考えています。

最初に、これらのカラーをプロジェクトにインポートしましょう。

colors.dart を作成する

colors.dart という名前の新しい dart ファイルを lib に作成します。マテリアル コンポーネントをインポートして、カラーの const 値を追加します。

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;

カスタムのカラーパレット

このカラーテーマは、デザイナーがカスタムカラーを使って作成したものです(下記の画像を参照)。これには Shrine のブランドから選択されたカラーが含まれており、Material Theme Editor に適用されています。Material Theme Editor では、それらのカラーが拡張され、より完全なパレットが作成されています(これらのカラーは、2014 マテリアル カラーパレットに由来するものではありません)。

Material Theme Editor では、カラーは数字のラベルが付いたシェードに分類され、各カラーのラベルは 50、100、200、....900 となっています。Shrine では、ピンクの見本からの 50、100、300 と、ブラウンの見本からの 900 のシェードのみを使用します。

b9170eb94fd3b106.jpeg f8b4b97f898f154e.png

ウィジェットの各カラー パラメータは、これらのパターンのカラーにマッピングされます。たとえば、入力をアクティブに受け取ったときのテキスト フィールドの装飾の色は、テーマのプライマリ カラーになります。その色のアクセシビリティが低い(背景に対して見づらい)場合は、代わりに PrimaryVariant を使用してください。

これらのバリエーションは 2014 年版のマテリアル ガイドライン用に作成されたものですが、現在のガイドライン(カラーシステムの記事)と MDC-Flutter でも引き続き利用できます。これらのバリエーションをコード内で利用するには、ベースカラーに続いてシェード(通常は百単位の値)を呼び出します。たとえば、ピンクの 400 を取得するには、コマンド Colors.pink[400] を使用します。

これらのパレットをデザインとコードに使用すると、完璧な効果が得られます。ブランディングに適したカラーがすでにある場合は、パレット生成ツールまたは Material Theme Editor を使用して、調和のとれたパレットを独自に生成できます。

使用するカラーを決めたので、それらを UI に適用できます。そのためには、ウィジェット階層の最上位にある MaterialApp インスタンスに適用する ThemeData ウィジェットの値を設定します。

ThemeData.light() をカスタマイズする

Flutter には組み込みのテーマがいくつかあります。ライトテーマはそのひとつです。ThemeData ウィジェットをゼロから作成する代わりに、ライトテーマをコピーし、アプリ用にカスタマイズするために値を変更します。

app.dart.colors.dart をインポートします。

import 'colors.dart';

次に、ShrineApp クラスのスコープ外にある app.dart に以下のコードを追加します。

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

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

ここで、ShrineApp の build() 関数(MaterialApp ウィジェット内にあります)の末尾に theme: を設定し、新しいテーマを指定します。

  // TODO: Add a theme (103)
  theme: _kShrineTheme, // New code

プロジェクトを保存します。ログイン画面は次のようになります。

Android

iOS

ピンクと茶色のテーマが設定された Shrine ログインページ

ピンクと茶色のテーマが設定された Shrine ログインページ

ホーム画面は次のようになります。

Android

iOS

ピンクと茶色のテーマが設定された Shrine 商品グリッドページ

ピンクと茶色のテーマが設定された Shrine 商品グリッドページ

5. タイポグラフィとラベルのスタイルを変更する

デザイナーは、色の変更に加えて、使用可能な特定のタイポグラフィも用意してくれています。Flutter の ThemeData には 3 つのテキストテーマがあります。各テキストテーマは、「見出し」や「タイトル」などのテキスト スタイルのコレクションです。アプリでは、いくつかのスタイルを使用し、値の一部を変更します。

テキストテーマをカスタマイズする

プロジェクトにフォントをインポートするため、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 個分のインデントを使用して、スペースに置き換えてください。次の行では 2 個のスペースに置き換えます。

fonts:

次の行では 4 個のスペースに置き換えます。

family: Rubik

以降の行も同様に修正します。

Mapping values are not allowed here」というメッセージが表示された場合は、問題がある行のインデントと、その上の行のインデントをチェックします。

login.dart で、Column() 内のコードを次のように変更します。

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

app.dart で、_buildShrineTheme() の後に次のコードを追加します。

// TODO: Build a Shrine Text Theme (103)
TextTheme _buildShrineTextTheme(TextTheme base) {
  return base.copyWith(
    headline5: base.headline5!.copyWith(
      fontWeight: FontWeight.w500,
    ),
    headline6: base.headline6!.copyWith(
      fontSize: 18.0,
    ),
    caption: base.caption!.copyWith(
      fontWeight: FontWeight.w400,
      fontSize: 14.0,
    ),
    bodyText1: base.bodyText1!.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,
),

プロジェクトを保存します。フォントを変更したので、今回はアプリの再起動も行います(これをホット リスタートといいます)。

Android

iOS

テキストテーマが適用された Shrine 商品グリッドページ

テキストテーマが適用された Shrine 商品グリッドページ

ログイン画面とホーム画面でテキストの外観が異なっています。一部のテキストでは 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.caption,
  ),
  // End new code
],

テキストを中央揃えにして位置を下げる

ラベルを中央揃えにして、テキストを各画像の下部ではなく、各カードの下部に揃えましょう。

ラベルをメインの軸の終端(下部)に移動し、中央揃えに変更します。

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

プロジェクトを保存します。

Android

iOS

テキストの配置を変えた Shrine 商品グリッドページ

テキストの配置を変えた Shrine 商品グリッドページ

これで外観が大幅に改善されました。

テキスト フィールドのテーマを設定する

InputDecorationTheme を使用して、テキスト フィールドの装飾にテーマを設定することもできます。

app.dart_buildShrineTheme() メソッドで inputDecorationTheme: 値を指定します。

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

現在、テキスト フィールドの装飾は filled になっています。これを削除しましょう。filled を削除して inputDecorationTheme を指定すると、テキスト フィールドに枠線付きスタイルが設定されます。

login.dartfilled: 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,
),

ホット リスタートします。[Username] フィールドがアクティブになっているとき(フィールドに入力中のとき)のログイン画面は次のようになります。

Android

iOS

[Username] フィールドがフォーカスされている Shrine ログインページ

[Username] フィールドがフォーカスされている Shrine ログインページ

テキスト フィールドに入力します。境界線とフローティング ラベルがプライマリ カラーでレンダリングされます。しかし、それらは簡単には見分けられません。色のコントラストが不明瞭なピクセルを識別しづらい人にとっては、アクセシビリティが低い状態です(詳しくは、マテリアル ガイドラインのカラーに関する記事の「アクセシビリティの高いカラー」をご覧ください)。

app.dartinputDecorationTheme: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,
  ),
),

最後に、[Cancel] ボタンを用意して、セカンダリ カラーを使用してコントラストを強くしてみましょう。

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

プロジェクトを保存します。

Android

iOS

[CANCEL] が使用可能な Shrine ログインページ

[CANCEL] が使用可能な Shrine ログインページ

6. 高度を調整する

ここまでは、Shrine に合った特定の色とタイポグラフィを使用してページのスタイルを設定しました。次は、高度を調整してみましょう。

[NEXT] ボタンの高度を変更する

ElevatedButton のデフォルトの高度は 2 です。この値を大きくしてみましょう。

login.dart で、NEXT ElevatedButton に style: 値を追加します。

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

プロジェクトを保存します。

カードの高度を調整する

現在、カードはサイトのナビゲーションの横の白いサーフェス上にあります。

home.dart で、カードに elevation: 値を追加します。

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

プロジェクトを保存します。

Android

iOS

カードに高度のない Shrine 商品グリッドページ

カードに高度のない Shrine 商品グリッドページ

カードの下のシャドウが除去されました。

7. シェイプを追加する

Shrine には、八角形または四角形のシェイプを持つ要素が定義された、お洒落な幾何学的スタイルがあります。ホーム画面のカードと、ログイン画面のテキスト フィールドおよびボタンに、それらのシェイプのスタイルを実装しましょう。

ログイン画面のテキスト フィールドのシェイプを変更する

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(
    primary: Theme.of(context).colorScheme.secondary,
    shape: const BeveledRectangleBorder(
      borderRadius: BorderRadius.all(Radius.circular(7.0)),
    ),
  ),
),

TextButton には目に見えるシェイプがないのに、枠線のシェイプを追加するのはなぜでしょうか?それは、タップされたときにリップル アニメーションが同じシェイプに収まるようにするためです。

次に、[NEXT] ボタンに同じシェイプを追加します。

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

すべてのボタンのシェイプを変更する場合は、app.dartelevatedButtonTheme または textButtonTheme を使用します。これは学習の課題として取っておきましょう。

ホット リスタートします。

Android

iOS

シェイプのテーマ設定が適用された Shrine ログインページ

シェイプのテーマ設定が適用された Shrine ログインページ

8. レイアウトを変更する

次に、レイアウトを変更して、カードをさまざまなアスペクト比とサイズで表示し、各カードの違いを際立たせましょう。

GridView を AsymmetricView に置き換える

非対称レイアウト用のファイルはすでに作成済みです。

home.dart で、次の import を追加します。

import 'supplemental/asymmetric_view.dart';

_buildGridCards を削除して、body を置き換えます。

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

プロジェクトを保存します。

Android

iOS

非対称で横方向にスクロール可能なレイアウトの Shrine 商品ページ

非対称で横方向にスクロール可能なレイアウトの Shrine 商品ページ

織物のようなパターンで商品を横方向にスクロールできるようになりました。

9. 別のテーマを試す(オプション)

色はブランドを表現するための強力な手段です。色を少し変えるだけで、ユーザー エクスペリエンスに大きく影響します。これを試すために、ブランドのカラーパターンを若干違うものに変更すると Shrine がどのように見えるかを確認してみましょう。

色を変更する

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,
    ),
    inputDecorationTheme: const InputDecorationTheme(
      border: CutCornersBorder(),
      focusedBorder: CutCornersBorder(
        borderSide: BorderSide(
          width: 2.0,
          color: kShrinePurple,
        ),
      ),
      floatingLabelStyle: TextStyle(
        color: kShrinePurple,
      ),
    ),
  );
}

ホット リスタートします。新しいテーマが表示されます。

Android

iOS

紫と白のテーマが適用された Shrine ログインページ

紫と白のテーマが適用された Shrine ログインページ

Android

iOS

紫と白のテーマが適用された Shrine 商品ページ

紫と白のテーマが適用された Shrine 商品ページ

外観が大きく変わりました。app.dart's _buildShrineTheme をこのステップの前の状態に戻してみましょう。あるいは、104 のスターター コードをダウンロードします。

10. 完了

以上で、デザイナーのデザイン仕様に近いアプリが作成されました。

次のステップ

この Codelab では、MDC コンポーネントとして、テーマ、タイポグラフィ、高度、シェイプを使用しました。MDC-Flutter ライブラリには、もっと多くのコンポーネントとサブシステムがあります。

次は、supplemental ディレクトリにあるファイルを詳しく調べて、水平スクロールが可能な非対称レイアウト グリッドを作成する方法を学習します。

計画しているアプリのデザインの要素に、MDC ライブラリのコンポーネントがない場合はどうしますか?MDC-104: マテリアル デザインの高度なコンポーネントでは、MDC ライブラリを使用して、特定の外観を備えたカスタム コンポーネントを作成する方法を示します。

この Codelab を完了するためにそれなりの時間と労力を必要とした

非常にそう思う そう思う どちらとも言えない そう思わない まったくそう思わない

今後もマテリアル コンポーネントを使用したい

非常にそう思う そう思う どちらとも言えない そう思わない まったくそう思わない