Flutter-テーマの拡張

初めに

独自なウィジェットを作成した場合、そのウィジェットのテーマ(色・フォントなど)をウィジェット内に直接実装するか、ThemeDataの元で管理をするか選択することになると思う。

ThemeData内の既定な情報を使うのであれば、実装の手間を考えなければそれを使えばいいので特に問題ないが、実値、例えばフォントサイズ、フォント種別、色等を個別に指定する場合には問題が出てくる。

まず色に関しては、ライト・ダークのBrightness属性による変化が大きい。
フォントサイズ・種別は、Material2/3による変化が大きいし、またプラットフォームの差も発生してくる可能性もある。

それらをウィジェット内でハードコードすると、テーマの追加やBrightnessの追加(今後あるとしたらハイコントラストモードかな。MaterialAppにそういうテーマの入力が可能になっているので)があった場合に対応が難しくなる。

そこで、ウィジェット内のテーマ情報をクラス化して管理するという方法をとれるThemeExtensionが必要になってくる。

作り方

ThemeExtension class – material library – Dart API (flutter.dev)

簡易的かもしれないけど、実装のほとんどはAPIヘルプの中に記載されている。
それを参考にすればいいのだけど、どういったものを入れ込んだ方が良いのかというのと、追加で実装しておいた方が良いのを記載する。

入れ込む情報

基本的に何でもいいと思っている。

ThemeDataの種類、Brightness属性やMaterial2/3の種別ごとに変えたいような情報を何でも入れてしまう。

一般的には、色やフォントスタイルなんかが考えられるが、それ以外にpaddingの幅などの情報もありだと思う。
Material2/3の区別で寸法のようなものも変えられているから。

作成

基本的な作り方についてはAPI説明書の方を参照。

class クラス名 extends ThemeExtension<クラス名> {}

上記の要領でクラスを作成し、その中に属性をfinalで追加していく。

最後、コンストラクタ、copyWith、lerpの仮想関数を実装する。

finalな属性を追加したら、それに合わせてコンストラクタ、copyWith、lerpを作ってくれるものが欲しいなと感じるけど、そこまで頻繁に変更したり多くを作ったりするものではないと思うので必要性はあまり感じない。手書きで十分だと思う。

factoryメソッド

コンストラクタでは属性へ代入するためのrequiredな設定のみになり、実際の値の構築・計算は外でという話となる記述がWEB上では多い。

そうなると、どう作るかという部分がこのクラス内で完結できず、実装が複数場所にわたってしまう懸念がある。
そこで、情報ベースを渡して、テーマ情報を構築しカスタムテーマを作成してしまう部分をfactoryにしてしまおうというのである。

factoryに渡す情報としては、最低限ベースのテーマ、プラスアルファとしてベースのテーマ内に情報が入っているとはいえ、BrightnessとuseMaterial3は外部から渡せるようにしておいた方が良いかもしれない。

  factory CustomTheme.from(ThemeData base,
      {Brightness? mode, bool? useMaterial3}) {
    mode ??= base.brightness;
    useMaterial3 ??= base.useMaterial3;
    return CustomTheme(
        b2Title:
            base.textTheme.bodyMedium!.copyWith(fontWeight: FontWeight.bold),
        pNoEntry: base.textTheme.bodyLarge!.copyWith(
            fontWeight: FontWeight.bold, color: base.colorScheme.secondary));
  }

ThemeDataのextensionsにはファクトリーで作ったものを渡すようにする。

これにより、情報自体の生成もこのクラス内に隠蔽できる。

ofメソッド

実際のウィジェットで作ったカスタムテーマを取り出そうとする場合は、以下の様な記述になると思う。

Theme.of(context).extension<CustomTheme>()!.プロパティ名

それでもいいのだけど使う側で「!」を書きたくない、もう少し実装量を減らしたいということを考えてしまうので、この部分もカスタムテーマクラス内に入れ込んでしまう。

  static CustomTheme of(BuildContext context) =>
      Theme.of(context).extension<CustomTheme>()!;

こうすることで使う側は以下の様な実装になる。

CustomTheme.of(context).プロパティ名

ホットリロード対応

factoryにしている部分のテーマを修正しても、そのままでは画面に反映されない。
クラスはホットリロード対応しているので再読み込みしてくれるのだけど、テーマ自体の作り直しはしてくれないから。

カスタムテーマクラスのホットリロードに対応するにはテーマの再作成&読み込みの機能を別途アプリケーションに保持しておく必要がある。

コメント

タイトルとURLをコピーしました