tips-getx-material-design3
tips-getx-material-design3

效果展示

  • 样式自定义

  • Material 样式调试工具




本节目标

  • 了解 Material Design 设计样式表
  • Flutter 中自定义样式(全局、局部)
    • 颜色
    • 字体
    • 组件

视频

https://www.bilibili.com/video/BV1aT4y1S7pB

代码

参考

正文

了解 Material 样式表

导入你的 figma

  • 颜色、字体
figma颜色字体
figma颜色字体
  • 组件
figma组件
figma组件

Flutter 中一步步定制你的样式

第一步: 生成 colorScheme,创建 ThemeData

导出 dart 格式导入你的项目

  • lib/color_schemes.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import 'package:flutter/material.dart';

const lightColorScheme = ColorScheme(
brightness: Brightness.light,
primary: Color(0xFF6750A4),
onPrimary: Color(0xFFFFFFFF),
primaryContainer: Color(0xFFEADDFF),
onPrimaryContainer: Color(0xFF21005D),
secondary: Color(0xFF625B71),
onSecondary: Color(0xFFFFFFFF),
secondaryContainer: Color(0xFFE8DEF8),
onSecondaryContainer: Color(0xFF1D192B),
tertiary: Color(0xFF7D5260),
onTertiary: Color(0xFFFFFFFF),
tertiaryContainer: Color(0xFFFFD8E4),
onTertiaryContainer: Color(0xFF31111D),
error: Color(0xFFB3261E),
errorContainer: Color(0xFFF9DEDC),
onError: Color(0xFFFFFFFF),
onErrorContainer: Color(0xFF410E0B),
background: Color(0xFFFFFBFE),
onBackground: Color(0xFF1C1B1F),
surface: Color(0xFFFFFBFE),
onSurface: Color(0xFF1C1B1F),
surfaceVariant: Color(0xFFE7E0EC),
onSurfaceVariant: Color(0xFF49454F),
outline: Color(0xFF79747E),
onInverseSurface: Color(0xFFF4EFF4),
inverseSurface: Color(0xFF313033),
inversePrimary: Color(0xFFD0BCFF),
shadow: Color(0xFF000000),
);

const darkColorScheme = ColorScheme(
brightness: Brightness.dark,
primary: Color(0xFFD0BCFF),
onPrimary: Color(0xFF381E72),
primaryContainer: Color(0xFF4F378B),
onPrimaryContainer: Color(0xFFEADDFF),
secondary: Color(0xFFCCC2DC),
onSecondary: Color(0xFF332D41),
secondaryContainer: Color(0xFF4A4458),
onSecondaryContainer: Color(0xFFE8DEF8),
tertiary: Color(0xFFEFB8C8),
onTertiary: Color(0xFF492532),
tertiaryContainer: Color(0xFF633B48),
onTertiaryContainer: Color(0xFFFFD8E4),
error: Color(0xFFF2B8B5),
errorContainer: Color(0xFF8C1D18),
onError: Color(0xFF601410),
onErrorContainer: Color(0xFFF9DEDC),
background: Color(0xFF1C1B1F),
onBackground: Color(0xFFE6E1E5),
surface: Color(0xFF1C1B1F),
onSurface: Color(0xFFE6E1E5),
surfaceVariant: Color(0xFF49454F),
onSurfaceVariant: Color(0xFFCAC4D0),
outline: Color(0xFF938F99),
onInverseSurface: Color(0xFF1C1B1F),
inverseSurface: Color(0xFFE6E1E5),
inversePrimary: Color(0xFF6750A4),
shadow: Color(0xFF000000),
);

分别定义了 ColorScheme 类型的,明亮暗色 两种色彩定义

  • lib/theme.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
import 'package:flutter/material.dart';

import 'color_schemes.dart';

class AppTheme {
static ThemeData light = ThemeData(
colorScheme: lightColorScheme,
);

static ThemeData dark = ThemeData(
colorScheme: darkColorScheme,
);
}

创建 明亮暗色两种ThemeData样式对象
分别设置 colorScheme 属性为上面的 lightColorScheme darkColorScheme 色彩定义

第二步: 切换 theme 效果

  • 加入 GetX pub 包

https://pub.flutter-io.cn/packages/get

1
> flutter pub add get
  • 新增 lib/global.dart 全局变量管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import 'package:get/get.dart';

import 'theme.dart';

class GlobalService extends GetxService {
static GlobalService get to => Get.find();

// 是否黑暗模式
final RxBool _isDarkModel = Get.isDarkMode.obs;
bool get isDarkModel => _isDarkModel.value;

// 切换模式
void switchThemeModel() {
_isDarkModel.value = !_isDarkModel.value;
Get.changeTheme(
_isDarkModel.value == true ? AppTheme.dark : AppTheme.light,
);
}
}
  • 修改 lib/main.dart main 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void main() {
appInit();
}

appInit() async {
// 初始 fluuter 引擎
WidgetsFlutterBinding.ensureInitialized();

// 初始
Get.put<GlobalService>(GlobalService());

// 启动
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',

// 初始样式
theme:
GlobalService.to.isDarkModel == true ? AppTheme.dark : AppTheme.light,

// home 界面
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

ensureInitialized 会先绑定 Flutter 引擎和原生端的接口,为后续的组件初始做准备

  • 加入 lib/main.dart 切换按钮
1
2
3
4
5
6
7
8
9
10
11
12
floatingActionButton: Obx(() {
return FloatingActionButton(
onPressed: () {
GlobalService.to.switchThemeModel();
},
child: Icon(
GlobalService.to.isDarkModel == true
? Icons.dark_mode
: Icons.light_mode,
),
);
}),

使用 Obx 方式响应按钮切换

第三步: 自定义主题色 MaterialColor

  • 新增 lib/string_to_material_color.dart 字符串转 MaterialColor 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/// 字符串转 Color 类型
/// Color color = stringToColor("40c254");
Color stringToColor(String source) {
return Color(int.parse(source, radix: 16) | 0xFF000000);
}

/// 字符串转 MaterialColor
/// MaterialColor materialColor = stringToMaterialColor("40c254");
MaterialColor stringToMaterialColor(String source) {
Color color = stringToColor(source);

List<double> strengths = <double>[.05];
Map<int, Color> swatch = <int, Color>{};
final int r = color.red, g = color.green, b = color.blue;

for (int i = 1; i < 10; i++) {
strengths.add(0.1 * i);
}
for (var strength in strengths) {
final double ds = 0.5 - strength;
swatch[(strength * 1000).round()] = Color.fromRGBO(
r + ((ds < 0 ? r : (255 - r)) * ds).round(),
g + ((ds < 0 ? g : (255 - g)) * ds).round(),
b + ((ds < 0 ? b : (255 - b)) * ds).round(),
1,
);
}
return MaterialColor(color.value, swatch);
}
  • 选取颜色

https://www.color-hex.com/

  • 设置 lib/theme.dart light 对象的属性 primarySwatch
1
2
3
4
5
6
7
class AppTheme {
// 自定义主色
static MaterialColor myColor = stringToMaterialColor("008744");

static ThemeData light = ThemeData(
primarySwatch: myColor,
);

第四步: 全局定义组件样式

  • 修改 lib/theme.dart , appBarTheme 属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class AppTheme {

static ThemeData light = ThemeData(
colorScheme: lightColorScheme,

// 全局 - 自定义 AppBarTheme
appBarTheme: ThemeData.light().appBarTheme.copyWith(
backgroundColor: Colors.yellow,
titleTextStyle: ThemeData.light().textTheme.titleLarge?.copyWith(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
);

copyWith 方法将会把其它属性一起复制

第五步: 局部定义组件样式

  • 修改 lib/main.dart, MyHomePage 界面的 build 方法
1
2
3
4
5
6
7
8
9
10
// 通过 GetX 的 theme 属性,读取当前样式 `ThemeData`
ThemeData theme = Get.theme;

return Theme(
data: theme.copyWith(
appBarTheme: theme.appBarTheme.copyWith(
backgroundColor: theme.colorScheme.tertiary,
),
),
child: Scaffold(

Theme 包裹需要局部使用的组件, 设置 data 属性

总结

本文介绍了 Material Design 3 中的样式表,以及如何在 Flutter 中如何修改全局、局部的样式,MaterialCorlor 和 Color 的区别以及如何使用。

我们可以发现如果和设计师协调好 颜色、字体、组件交互的样式标准,然后再适配下 Material Design ,样式配置将会快很多,很多时候样式调整给客户的是一种新鲜感,比如节假日、特殊日子里色调改下就行。


© 猫哥

订阅号
订阅号