Edited at

FlutterのThemeを可能な限り柔軟性を持たせてみた


はじめに

Flutterのテーマはマテリアルデザインに100%準拠している場合はとても便利だと思うんですが、例えばbuttonThemeが一つしか設定できなくて柔軟性に欠けているように思えます。画面によってもっといろんなデザインに変えたい!(Buttonの形を変えたい、フォントスタイルもっと柔軟に変えたい)

ということで今回は、Themeをもっと柔軟に使えるようにしてみました。


最適化


ファイル構成

└─lib

└─theme
├─theme.dart
├─text_theme_custom.dart
├─color_theme_custom.dart
└─style_theme_custom.dart

ファイル構成はこんな感じでText、Color、Styleそれぞれ別ファイルで定義するようにしました。


theme.dart

theme.dartではThemeDataを返却するメソッドを定義します。


theme.dart

ThemeData createTheme() {

return ThemeData(
primarySwatch: ColorThemeCustom.customSwatch,
primaryColor: ColorThemeCustom.customPrimaryColor,
brightness: Brightness.light,
primaryColorBrightness: Brightness.light,
buttonTheme: StyleThemeCustom.createButtonTheme().copyWith(shape: StyleThemeCustom.round),
textTheme: textTheme,
primaryTextTheme: textTheme,
accentTextTheme: textTheme,
);
}



text_theme_custom.dart

デフォルト値をcreateTextThemeとして定義。フォントサイズ、フォントカラーは柔軟に変更できるように別途定義します。


text_theme_custom.dart

class TextThemeCustom {

TextThemeCustom._();

static TextTheme createTextTheme() {
return TextTheme(
headline: TextStyle(
color: ColorThemeCustom.theme["black"],
fontWeight: FontWeight.w400,
fontStyle: FontStyle.normal,
).merge(font_size_18),
body2: TextStyle(
color: ColorThemeCustom.theme["black"],
height: 1.0
).merge(font_normal).merge(font_size_15),
body1: TextStyle(
color: Color(0x8A1A1512),
).merge(font_normal).merge(font_size_15),
button: TextStyle(
color: ColorThemeCustom.theme["white"],
).merge(font_medium).merge(font_size_17),
title: TextStyle(
color: ColorThemeCustom.theme["black"],
).merge(font_medium).merge(font_size_17),
subtitle: TextStyle(
color: ColorThemeCustom.theme["black"],
).merge(font_medium).merge(font_size_16),
);
}
static const TextStyle font_normal = const TextStyle(
fontSize: 16.0,
fontFamily: 'NotoSansCJKjp',
fontWeight: FontWeight.normal,
fontStyle: FontStyle.normal,
);
static const TextStyle font_medium = const TextStyle(
fontSize: 16.0,
fontFamily: 'NotoSansCJKjp',
fontWeight: FontWeight.w500,
fontStyle: FontStyle.normal,
);
static const TextStyle font_bold = const TextStyle(
fontSize: 16.0,
fontFamily: 'NotoSansCJKjp',
fontWeight: FontWeight.bold,
fontStyle: FontStyle.normal,
);
static const TextStyle font_size_15 = const TextStyle(fontSize: 15.0);
static const TextStyle font_size_16 = const TextStyle(fontSize: 16.0);
static const TextStyle font_size_17 = const TextStyle(fontSize: 17.0);
static const TextStyle font_size_18 = const TextStyle(fontSize: 18.0);
static const TextStyle font_size_22 = const TextStyle(fontSize: 22.0);
static const TextStyle font_size_40 = const TextStyle(fontSize: 40.0);

static const TextStyle font_color_primary = const TextStyle(color: ColorThemeCustom.customPrimaryColor);
}



color_theme_custom.dart

Primaryカラーや、Swatch、その他いろんな用途用にMapでカラーを定義します。


color_theme_custom.dart

class ColorThemeCustom {

ColorThemeCustom._();

static const int customPrimaryValue = 0xFFF5A076;
static const Color customPrimaryColor = const Color(customPrimaryValue);
static const MaterialColor customSwatch = const MaterialColor(
customPrimaryValue,
const <int, Color> {
50: const Color(0xFFFFDDC1),
100: const Color(0xFFFFD3B7),
200: const Color(0xFFFFC9AE),
300: const Color(0xFFFFBFA4),
400: const Color(0xFFFFB59B),
500: const Color(0xFFFFAB91),
600: const Color(0xFFF29F86),
700: const Color(0xFFE4937A),
800: const Color(0xFFD7876F),
900: const Color(0xFFC97B63),
}
);
static const Map<String, Color> theme = const <String, Color>{
"white": const Color(0xFFFFFFFF),
"black": const Color(0xdd000000),
};
}



style_theme_custom.dart

ButtonのデフォルトテーマをcreateButtonThemeとして定義、それ以外にround型ボタンやsquare型のボタンを使いたかったので別途StadiumBorder、RoundedRectangleBorder型の定数を定義。


style_theme_custom.dart

class StyleThemeCustom {

StyleThemeCustom._();

static ButtonThemeData createButtonTheme() {
return ButtonThemeData(
minWidth: 88.0,
height: 36.0,
padding: EdgeInsets.only(top: 0.0, bottom: 0.0, left: 16.0, right: 16.0),
alignedDropdown: false,
disabledColor: ColorThemeCustom.theme["black"],
highlightColor: ColorThemeCustom.theme["black"],
);
}

static const StadiumBorder round = StadiumBorder(
side: BorderSide(
color: ColorThemeCustom.theme["black"],
width: 0.0,
style: BorderStyle.none,
),
);

static const RoundedRectangleBorder square = RoundedRectangleBorder(
side: BorderSide(
color: ColorThemeCustom.theme["black"],
width: 0.0,
style: BorderStyle.none,
),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
);
}



使い方

まずは今回作成したThemeDataをmain.dartで適用してあげます。


main.dart

class MyApp extends StatelessWidget {

// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Startup Name Generator',
theme: createTheme(),
home: RandomWords(),
);
}
}

margeを使うことでthemeのマージ元をマージ先で上書きできます。

// テキスト

Text(
"テキスト",
style: Theme.of(context).textTheme.body1
.merge(TextThemeCustom.font_size_22)
.merge(TextThemeCustom.font_color_primary),
);
// or
Text(
"テキスト",
style: TextThemeCustom.font_normal
.merge(TextThemeCustom.font_size_22)
.merge(TextThemeCustom.font_color_primary),
);

// ボタン
RaisedButton(
shape: StyleThemeCustom.square,
onPressed: _pushSaved
);


終わりに

いかがでしたか?もっと便利な方法あったらぜひ教えて下さい!


参考

https://qiita.com/granoeste/items/352d19157c21cd35e21f