雖然理論上 Material Design 要使用內建的顏色變數,如 primary
、secondary
…,但實務上 Designer 可能並不使用這些變數,我們也可以在 Material Design 使用 Figma 的變數。
Version
Flutter 3.24
Flutter
- Android 與 iOS 都成功使用 Material Design 的自訂顏色變數
Custom Colors
utils/custom_colors.dart
import 'package:flutter/material.dart';
import '../constants/dark_custom_colors.dart';
import '../constants/light_custom_colors.dart';
ThemeData getThemeData(Brightness brightness) {
return ThemeData(
brightness: brightness,
useMaterial3: true,
extensions: <ThemeExtension<dynamic>>[
brightness == Brightness.light ? lightCustomColors : darkCustomColors,
],
);
}
class CustomColors extends ThemeExtension<CustomColors> {
final Color grey100;
final Color darkRed;
const CustomColors({
required this.grey100,
required this.darkRed,
});
CustomColors copyWith({Color? grey100, Color? darkRed}) {
return CustomColors(
grey100: grey100 ?? this.grey100,
darkRed: darkRed ?? this.darkRed,
);
}
CustomColors lerp(ThemeExtension<CustomColors>? other, double t) {
if (other is! CustomColors) return this;
return CustomColors(
grey100: Color.lerp(grey100, other.grey100, t)!,
darkRed: Color.lerp(darkRed, other.darkRed, t)!,
);
}
}
- 要使用自訂顏色變數,必須先遵循 Material Design 先建立
CustomColors
class
Line 17
final Color grey100;
final Color darkRed;
- 在
CustomColors
class 將自訂顏色變數定義成 property,有幾個顏色就要定義幾個 property
Line 20
const CustomColors({
required this.grey100,
required this.darkRed,
});
- 在
CustomColors
class 的 constructor 定義自訂顏色變數,有幾個顏色就要定義幾個
Line 25
copyWith({Color? grey100, Color? darkRed}) {
return CustomColors(
grey100: grey100 ?? this.grey100,
darkRed: darkRed ?? this.darkRed,
);
}
CustomColors
- 在
CustomColors
class overridecopyWith()
,有幾個顏色就要定義幾個
Line 33
lerp(ThemeExtension<CustomColors>? other, double t) {
if (other is! CustomColors) return this;
return CustomColors(
grey100: Color.lerp(grey100, other.grey100, t)!,
darkRed: Color.lerp(darkRed, other.darkRed, t)!,
);
}
CustomColors
- 在
CustomColors
class overridelerp()
,有幾個顏色就要定義幾個
Line 6
ThemeData getThemeData(Brightness brightness) {
return ThemeData(
brightness: brightness,
useMaterial3: true,
extensions: <ThemeExtension<dynamic>>[
brightness == Brightness.light ? lightCustomColors : darkCustomColors,
],
);
}
- 使用 Extension Method 將自訂顏色變數加到
ThemeData
Light Custom Colors
constants/light_custom_colors.dart
import 'package:flutter/material.dart';
import '../utils/custom_colors.dart';
var lightCustomColors = const CustomColors(
grey100: Color(0xFFF8F8F8),
darkRed: Color(0xFFFF0000),
);
- 定義 Light Mode 的自訂顏色變數
Dark Custom Colors
constants/dark_custom_color.dart
import 'package:flutter/material.dart';
import '../utils/custom_colors.dart';
var darkCustomColors = const CustomColors(
grey100: Color(0xFF212529),
darkRed: Color(0xFFFFFF00),
);
- 定義 Dark Mode 的自訂顏色變數
Main
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_lab/utils/custom_colors.dart';
import 'home.dart';
void main() {
runApp(const App());
}
class App extends StatelessWidget {
const App({super.key});
Widget build(BuildContext context) {
return MaterialApp(
theme: getThemeData(Brightness.light),
darkTheme: getThemeData(Brightness.dark),
themeMode: ThemeMode.system,
home: const Home());
}
}
- 在
main.dart
使用自訂 theme
Line 13
build(BuildContext context) {
return MaterialApp(
theme: getThemeData(Brightness.light),
darkTheme: getThemeData(Brightness.dark),
themeMode: ThemeMode.system,
home: const Home());
}
Widget
theme
:由getThemeData()
定義 Light Mode 的 themedarkTheme
:由getThemeData()
定義 Dark Mode 的 themethemeMode
:預設使用系統所定義的 theme
Home
home.dart
import 'package:flutter/material.dart';
import 'utils/custom_colors.dart';
class Home extends StatefulWidget {
const Home({super.key});
State<Home> createState() => _Home();
}
class _Home extends State<Home> {
late CustomColors _customColors;
var _count = 0;
Widget build(BuildContext context) {
_customColors = Theme.of(context).extension<CustomColors>()!;
var appBar = AppBar(
title: const Text('Material Design Custom Color'),
);
var floatingActionButton = FloatingActionButton(
onPressed: () => setState(() => _count++),
backgroundColor: _customColors.grey100,
foregroundColor: _customColors.darkRed,
child: const Icon(
Icons.add,
),
);
var body = Center(
child: Text('$_count'),
);
return Scaffold(
appBar: appBar,
floatingActionButton: floatingActionButton,
body: body,
);
}
}
- 經典的 Counter
Line 12
class _Home extends State<Home> {
late CustomColors _customColors;
}
_customColors
:定義_customColors
為 field,且為late
,將在稍後建立
Line 17
Widget build(BuildContext context) {
_customColors = Theme.of(context).extension<CustomColors>()!;
}
_customColors
:在build()
內由context
根據 dark mode 或 light mode 建立
Line 24
var floatingActionButton = FloatingActionButton(
onPressed: () => setState(() => _count++),
backgroundColor: _customColors.grey100,
foregroundColor: _customColors.darkRed,
child: const Icon(
Icons.add,
),
);
backgrondColor
與foregroundColor
使用自訂的gray100
與darkRed
變數
Conclusion
- 之所以能使用 Material Design 又可使用自訂顏色變數,關鍵在於 Dart 支援 Extension Method
- 可將
customColor
定義成 field,如此所有地方都可直接存取