🎨

FlutterでColor Schemeを生成するWebアプリを作ってみた

2022/05/26に公開

先日、FlutterでMaterial Design 3に対応するという記事を書きました。
https://zenn.dev/enoiu/articles/6b754d37d5a272
上の記事では、Material Design 3に対応するために、colorSchemeをアプリに適用させました。

アプリに適用したなら、アプリアイコンやスクリーンショットでも、このcolorSchemeでの色を使いたい!
ということで、そのカラーコードを簡単に取得できるWebアプリを作ってみました。

https://colorscheme.enoiu.com/

この記事では、まずこのWebアプリの紹介を簡単にして、その後、このアプリを作った際の工夫等をまとめたいと思います。

追記:Android, iOS, MacOS版もリリースしました。アプリの詳細↓
https://enoiu.com/app/colorscheme/

主な機能


Color Schemeを表示。クリックでカラーコードをコピー。

カラーコードをHEX, #HEX, RGBから選択。

Color Scheme Seedを選択。この色をもとにColor Schemeを生成。

ダークモードでのColor Schemeも表示。

レスポンシブデザイン。

このアプリを作ったきっかけ

このアプリを作ったきっかけは、冒頭で書いたように、アプリアイコンやスクリーンショット(アプリストアに提出するやつ)にcolorSchemeの色を使いたかったからです。

実は、Material Theme Builderという公式ページでカラーコードを取得することができます。
しかし、このページでは、FlutterのようにcolorSchemeSeedを指定してcolorSchemeを生成するのではなく、Primary, Secondaryなどを指定する形式なので、Flutterを使っている私の用途には合わないものでした。

ということで、Flutter関連での使途を念頭に、colorSchemeのカラーコードを取得できるアプリを作ろうと思い立ちました。

UIの参考にしたアプリ・サイト

色をクリックするとカラーコードがコピーできる仕様については、以下のサイトを参考にしました。
https://materialui.co/colors
また、Flutter Samplesにある以下サイトも参考にしました。ソースコードも載っていてとても参考になりました。
https://flutter.github.io/samples/material_you.html

色 ListTile

色・Color Schemeでの名前・カラーコードを示すために、ListTileを使いました。
Color Schemeでの名前をtitleに、カラーコードをsubtitleに、色をtileColorに指定しています。
ListTileは、SizedBox等で高さを変えられないのがネックですが、きれいなUIを作る上でとても役立ちます。

このListTileをFlexibleで囲って、これをRowに入れると均等な横幅で表示されます。

Color Picker

Color Pickerには、以下のパッケージを使いました。
https://pub.dev/packages/flutter_colorpicker
Material Colorを簡単にピックできるようにしたかったので、MaterialPickerColorPickerToggleButtonで選べるようにしました。

ToggleButtonについて

Flutterで使えるToggleButtonは、まだMaterial Design 3に対応していません。
ただそれっぽい見た目にするために、以下のページを参考にカスタマイズしてみました。
https://m3.material.io/components/segmented-buttons/specs

List<bool> isSelected = [true, false];
//
ColorScheme scheme = ColorScheme.fromSeed(
  seedColor: color,
  brightness: darkMode ? Brightness.dark : Brightness.light);
//
Center(
  child: SizedBox(
      height: 40,
      child: ToggleButtons(
        borderColor: scheme.outline,
        selectedBorderColor: scheme.outline,
        fillColor: scheme.secondaryContainer,
        color: scheme.onSurface,
        selectedColor: scheme.onSecondaryContainer,
        borderRadius: BorderRadius.circular(20.0),
        onPressed: (int index) {
	  setState(() {
	  for (int buttonIndex = 0;
	      buttonIndex < isSelected.length;
	      buttonIndex++) {
	      if (buttonIndex == index) {
	        isSelected[buttonIndex] = true;
	      } else {
	        isSelected[buttonIndex] = false;
	      }
	    }
	  });
        },
        isSelected: isSelected,
        children: [
          SizedBox(
              width: 140,
              child: Row(
                  mainAxisAlignment:
                      MainAxisAlignment.center,
                  children: [
                    isSelected[0]
                        ? const Icon(Icons.check,
                            size: 18)
                        : Container(),
                    const Text('Material Picker')
                  ])),
          SizedBox(
              width: 140,
              child: Row(
                  mainAxisAlignment:
                      MainAxisAlignment.center,
                  children: [
                    isSelected[1]
                        ? const Icon(Icons.check,
                            size: 18)
                        : Container(),
                    const Text('Color Picker')
                  ])),
        ],
      )))

ThemeDataを動的に変更

ThemeDataのcolorSchemeSeedの色やDarkModeを動的に変更するために、adaptive_themeというパッケージを使っています。
https://pub.dev/packages/adaptive_theme
また、ピックしたColorをSharedPreferencesに保存しているのですが、これを起動時にThemeDataに反映させるため、runAppの前にSharedPreferencesを読み込んでいます。

estimateBrightnessForColor

Flutter samplesのMaterial Design 3のデモを見て、estimateBrightnessForColorというのを初めて知りました。
これは、特定の色からBrightnessを判定するものです。
https://api.flutter.dev/flutter/material/ThemeData/estimateBrightnessForColor.html
今回のアプリでは、Color Scheme Seed, Outline, Shadowの色の上にあるテキストの色を取得するために使っています。

Color contrastColor(Color c) {
  final brightness = ThemeData.estimateBrightnessForColor(c);
  switch (brightness) {
    case Brightness.dark:
      return Colors.white;
    case Brightness.light:
      return Colors.black;
  }
}

レスポンシブデザイン

レスポンシブデザインっていうほどでもないのですが、ウィンドウの横幅によってレイアウトやテキストを変えるようにしています。
まず、色のListTileの並びについては、通常はRowで3つ〜4つ並べていますが、横幅が狭い(880未満)とテキストが2段になってListTileの高さがばらばらになってしまうので、横幅が狭くなったらColumnになるようにしています。
また、ToggleButtonのテキストやAppBarのtitleなども横幅によって変えるようにしています。

bool isPC = (MediaQuery.of(context).size.width >= 880);
bool isMobile = (MediaQuery.of(context).size.width <= 450);

Webアプリとしてリリース

以下の記事でまとめています。
https://zenn.dev/enoiu/articles/e9dbc4d4b70a5c

まとめ

ということで、Webアプリ「Color Scheme Generator」の紹介と、このアプリを作った際の工夫等をまとめました。
このアプリを使って、ぼちぼちMaterial Design 3に対応しながら、アイコンやスクリーンショットを作成していきたいです。

https://colorscheme.enoiu.com/

基本PCでの利用を前提としていますが、スマホでの利用により最適化するために、Android, iOSアプリとしてもリリースするかも?しれないです。

追記 Android, iOS, MacOS版リリース

Android, iOS, MacOS版もリリースしました。
Android版↓
https://play.google.com/store/apps/details?id=com.enoiu.colorscheme
MacOS, iOS版↓
https://apps.apple.com/jp/app/colorscheme-for-m3/id1626395906?mt=12

Discussion