【Flutter】自動生成を駆使してデザインやテーマをまとめて管理できるパッケージを作った
Katana Theme
こんにちは。広瀬マサルです。
今回はテーマやデザイン周りの整備を行いました。
flutter_genのようなアセットやフォントの自動生成を行ないつつ
色やテキストスタイルなどを体系的に管理可能なパッケージ
を作成しました。
使い方をまとめたので興味ある方はぜひ使ってみてください!
katana_theme
はじめに
Flutterのテーマの扱いは複雑怪奇です。
FlutterではThemeData
にアプリのテーマがまとめられていますが、ThemeData内に様々なテーマが入っており、どのテーマがそもそも存在するのか、どのテーマを変更すればウィジェット内のどこが変わるのか非常に分かりづらいです。
Flutterは基本的にMaterialDesignをベースにしています。
そのためMaterialDesign用のカラースキームやテキストサイズを用いてデザインをシンプルに定義できるようなパッケージを作成しました。
合わせてflutter_genなどで使用されているassets
やfonts
のファイル構成からコードを自動生成する仕組みを導入し体系的に管理できるようにしています。
下記の機能が利用可能です。
- MaterialDesing3のColorSchemeで色を指定
- MaterialDesign3のTypeScaleで文字サイズを指定
- 上記をThemeDataに適用してアプリに指定できる機能
- assetsフォルダから画像へのパスおよびImageProviderを取得できるコードの自動生成
- fontsフォルダからフォントファミリーの指定が可能なコードの自動生成
- テーマからのグラデーションカラーの指定
- その他簡易的なデザイン変換用メソッド
下記のように記載可能です
final theme = AppTheme(
primary: Colors.blue,
secondary: Colors.cyan,
);
Text(
"test text",
style: theme.text.bodyMedium.withColor(theme.color.primary),
)
インストール
build_runnerを用いたコードジェネレーションを行うため下記のパッケージをインポートします。
flutter pub add katana_theme
flutter pub add --dev build_runner
flutter pub add --dev katana_theme_builder
実装
テーマ作成
まず@appTheme
のアノテーションをつけて、AppThemeData
を作成します。
@appThemeを利用するファイルにはpart ‘元のファイル名.theme.dart’
を追加してください。
// theme.dart
part 'theme.theme.dart';
final theme = AppThemeData();
AppThemeScopeの作成とMaterialAppへのテーマ適用
MaterialApp
などの上にAppThemeScope
を作成し、先程定義したAppThemeData
を渡します。
MaterialAppのthemeにはtheme.toThemeData()
を渡すことでアプリ内にAppThemeData内で定義したテーマを適用することができます。
// main.dart
AppThemeScope(
theme: theme,
child: MaterialApp(
home: const MyHomePage(
title: "Flutter Demo",
),
title: "Flutter Demo",
theme: theme.toThemeData(),
),
);
アセットの定義
下記の公式サイトを参考にpubspec.yamlを編集し、アプリ内でアセットを読み込めるようにしてください。
// pubspec.yaml
flutter:
assets:
- assets/images/
フォントの定義
下記の公式サイトを参考にpubspec.yamlを編集し、アプリ内でフォントを読み込めるようにしてください。
// pubspec.yaml
flutter:
fonts:
- family: RobotoMono
fonts:
- asset: fonts/RobotoMono-Regular.ttf
- asset: fonts/RobotoMono-Bold.ttf
weight: 700
コードジェネレーション
下記のコマンドを入力することで自動でコード生成を行います。
flutter pub run build_runner build --delete-conflicting-outputs
テーマの指定
AppThemeData
にテーマを指定することができます。
マテリアルデザインのカラースキームに従い下記の色を指定可能です。
- primary
- secondary
- tertiary
- primaryContainer
- secondaryContainer
- tertiaryContainer
- disabled
- outline
- error
- surface
- background
- onPrimary
- onSecondary
- onTertiary
- onPrimaryContainer
- onSecondaryContainer
- onTertiaryContainer
- onDisabled
- onSurface
- onBackground
- onError
さらに追加色として下記を設定可能です。
- weak
- 薄字の色。あまり目立たせたくない色に使用
- warning
- 注意の色。
- info
- ちょっとした情報を表示する際の色
- success
- 成功時の色。
- onWeak
-
weak
を背景色にしたときの文字色。
-
- onInfo
-
info
を背景色にしたときの文字色。
-
- onSuccess
-
success
を背景色にしたときの文字色。
-
- onWarning
-
warning
を背景色にしたときの文字色。
-
- splashColor
- ボタンをタップしたときのエフェクトカラー。
- shadow
- 影の色。
- inverseSurface
-
surface
の反転色。
-
- onInverseSurface
-
inverseSurface
を背景色にしたときの文字色。
-
マテリアルデザインのTypographyに従い下記のTypeScaleを指定可能です。
- displayLarge
- displayMedium
- displaySmall
- headlineLarge
- headlineMedium
- headlineSmall
- titleLarge
- titleMedium
- titleSmall
- bodyLarge
- bodyMedium
- bodySmall
- labelLarge
- labelMedium
- labelSmall
使い方
AppThemeData
はグローバルで定義されているのでどこからでも参照可能です。
AppThemeData
の中からは下記のテーマが取得可能です。
-
color
-
AppThemeData
作成時に定義したColorScheme。
-
-
text
-
AppThemeData
作成時に定義したTypeScale
-
-
asset
- コードジェネレーションで作成した
assets
フォルダ以下のアセット
- コードジェネレーションで作成した
-
font
- コードジェネレーションで作成したFontFamily。
-
widget
- インジケーターなどの固定ウェジェット定義。
- 後述の
テーマ拡張
で定義します。
class TestPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Title"), backgroundColor: theme.color.secondary),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:[
Center(child: CircleAvatar(backgroundImage: theme.asset.userIcon.provider)),
Text("User Name", style: theme.text.displayMedium)
]
)
);
}
}
テーマ拡張
アプリによっては特殊な色や文字サイズを使う場合もあります。
1箇所でよければ直接ウィジェットに指定してもよいのですが、アプリ内の複数箇所にまたがる場合はテーマで指定したほうが効率的です。
このパッケージではextension
を用いることにより新しいテーマを追加します。
下記のクラスをon
に指定してextension
を作成可能です。
-
ColorThemeData
- 色を追加します。
-
TextThemeData
- 文字サイズなどのTextStyleを追加します。
-
AssetThemeData
- 画像などのアセットを追加します。
-
WidgetThemeData
- インジケーターなどのウェジェットを追加します。
extension ColorThemeDataExtensions on ColorThemeData {
Color get myColor => Colors.red;
}
extension WidgetThemeDataExtensions on WidgetThemeData {
Widget get indicator => const LinearProgressIndicator();
}
応用した使い方
グラデーション
テーマにGradientColor
を指定することでグラデーションを適用することが可能です。
GradientColor
はなにもしないと通常色として認識されますが、toLinearGradient()
でグラデーションカラーとして利用可能です。
// theme.dart
final theme = AppThemeData(
primary: GradientColor(
Colors.red, // こちらが通常色として認識される
Colors.white,
)
);
// test_page.dart
class TestPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Title")),
body: Container(
decoration: BoxDecoration(
gradient: theme.color.primary.toLinearGradient(),
),
)
);
}
}
変換用メソッド
実際に使用する際にテーマのデータに若干修正を加えて出力可能です。
final Color darkenColor = theme.color.primary.darken(); // すこし色を暗くして出力
final Color lightenColor = theme.color.primary.lighten(); // すこし色を明るくして出力
final TextStyle smallizeText = theme.text.bodyMedium.smallize(); // すこし文字サイズを小さくして出力
final TextStyle largizeText = theme.text.bodyMedium.largize(); // すこし文字サイズを大きくして出力
final TextStyle changedFontSizeText = theme.text.bodyMedium.withSize(18); // フォントサイズを18にして出力
final TextStyle darkenText = theme.text.bodyMedium.darken(); // すこし文字色を暗くしてして出力
final TextStyle lightenText = theme.text.bodyMedium.lighten(); // すこし文字色を明るくして出力
final TextStyle changedFontColorText = theme.text.bodyMedium.withColor(theme.color.primary); // すこし文字色をプライマリーカラーにして出力
final TextStyle changedOpacityText = theme.text.bodyMedium.withOpacity(0.5); // 文字の透明度を半分にして出力
final TextStyle changedFontWeightText = theme.text.bodyMedium.withBold(); // 文字を太字にして出力
おわりに
自分で使う用途で作ったものですが実装の思想的に合ってそうならぜひぜひ使ってみてください!
また、こちらにソースを公開しているのでissueやPullRequestをお待ちしてます!
また仕事の依頼等ございましたら、私のTwitterやWebサイトで直接ご連絡をお願いいたします!
GitHub Sponsors
スポンサーを随時募集してます。ご支援お待ちしております!
Discussion