Flutter中关于色彩和主题的内容非常之多,我们需要理清不同的Color之间的异同,才能更好的开发Flutter应用。
MaterialColor
在ThemeData的构造函数中,我们可以发现两个很有意思的属性
代码语言:javascript复制MaterialColor? primarySwatch,
Color? primaryColor,
在Flutter创建的Demo中,Theme是这样设置的。
代码语言:javascript复制return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
有没有人很好奇,primarySwatch和primaryColor,到底要设置哪个?到底谁才是真正的「主色调」?
首先,MaterialColor并不等于Color,它是基于MaterialDesign而产生的一套颜色体系。
在这个颜色系统中,基色和明暗不同的10种颜色作为一组处理,从而形成了MaterialColor。
前面代码中的Colors.blue,实际上就是一个MaterialColor,我们来看下它的实现。
代码语言:javascript复制static const MaterialColor blue = MaterialColor(
_bluePrimaryValue,
<int, Color>{
50: Color(0xFFE3F2FD),
100: Color(0xFFBBDEFB),
200: Color(0xFF90CAF9),
300: Color(0xFF64B5F6),
400: Color(0xFF42A5F5),
500: Color(_bluePrimaryValue),
600: Color(0xFF1E88E5),
700: Color(0xFF1976D2),
800: Color(0xFF1565C0),
900: Color(0xFF0D47A1),
},
);
static const int _bluePrimaryValue = 0xFF2196F3;
由此可见,MaterialColor的10种颜色是怎么实现的。
事实上,MaterialColor的定义就是如此,一个基色,加上一个不同shade的Map。
如果你要自定义一个MaterialColor,那么这10种色调,也是必须要实现的。
❝除了MaterialColor以外,还有一个MaterialAccentColor,它和MaterialColor类似,但是只有5种色调。 ❞
Color
Colors:这个类是来自Material调色板的颜色。要在代码中访问它们,只需调用基色和shade值即可。
代码语言:javascript复制color: Colors.red
color: Colors.red[200]
Color:你可以将这个类用于Material调色板以外的颜色,因为它允许ARGB(Alpha, Red, Green, Blue)格式的颜色值。最常见的使用方法是像下面的代码这样传递十六进制颜色代码,其中0xFF代表完全不透明的颜色。
代码语言:javascript复制Color(0xFF42A5F5)
primarySwatch
接下来,我们继续来看前面提到的那个问题,为什么ThemeData中需要设置primarySwatch。
在theme_data的源代码中,我们可以发现这样的代码。
代码语言:javascript复制primarySwatch ??= Colors.blue;
primaryColor ??= isDark ? Colors.grey[900]! : primarySwatch;
final Brightness _primaryColorBrightness = estimateBrightnessForColor(primaryColor);
primaryColorLight ??= isDark ? Colors.grey[500]! : primarySwatch[100]!;
primaryColorDark ??= isDark ? Colors.black : primarySwatch[700]!;
从这里,我们就可以知道为什么在Demo中设不设置primarySwatch都会是蓝色的主题色的原因了。
那么一个具体的Flutter组件,是如何决定自己的主题的呢?以Appbar为例,我们在源代码中找到对应设置background的地方。
代码语言:javascript复制final Color backgroundColor = backwardsCompatibility
? widget.backgroundColor
?? appBarTheme.backgroundColor
?? theme.primaryColor
: _resolveColor(
states,
widget.backgroundColor,
appBarTheme.backgroundColor,
colorScheme.brightness == Brightness.dark ? colorScheme.surface : colorScheme.primary,
);
❝不要被这里茫茫多的问号搞昏了,复习一下Dart语法吧。
- 「?.」——代表非空访问,例如「myObject?.someProperty」,等价于——「(myObject != null) ? myObject.someProperty : null」
- 「??」——代表避空判断,例如「a ?? 3」a为空时,返回3
- 「??=」——同样是避空赋值,例如「a ??= 3」a为空时,a赋值为3
❞
了解了这些之后,你应该就能看懂上面的代码了,原来Appbar的background是经过很多场景来判断的,简而言之:
- 先判断是否在Appbar中设置了backgroundColor
- 再判断是否指定了AppBarTheme.backgroundColor,也就是针对Appbar进行的Theme覆盖
- 最后再根据是否黑夜模式来判断使用ColorScheme.primary还是ColorScheme.surface
大部分的Flutter组件,几乎都遵循这个判断流程,只是使用的Color类型不太一样。
但是,primaryColor并不是没用了,它可以用来更改组件的Theme,用于局部主题的使用。
代码语言:javascript复制Expanded(
child: Theme(
data: Theme.of(context).copyWith(primaryColor: Colors.red),
child: Container(
padding: const EdgeInsets.all(15.0),
color: Theme.of(context).primaryColor,
child: Text(
'This Container overrides primaryColor',
style: Theme.of(context).headline5,
),
)))
ColorScheme
色彩的千变万化,最终会导致Theme属性的膨胀,这是可以预见的,所以你可以看看ThemeData有多少属性需要配置就知道了。
在ThemeData的构造函数中,有超过70种的Color和Theme,这要全部通过手工来配置,将是一个非常大的工作量。
因此,Flutter引入了ColorScheme属性,它是一组基于Material规范的25种颜色(9种必选色),可用于配置大多数组件的颜色属性。Flutter团队计划用定义好的ColorScheme来设计材质组件的样式。要使用colorScheme,你必须调用ThemeData.from()构造函数。
代码语言:javascript复制ThemeData.from(
colorScheme: const ColorScheme.light().copyWith(
primary: const Color(0xff455a64),
primaryContainer: const Color(0xff1c313a),
secondary: const Color(0xffffc400),
secondaryContainer: const Color(0xffc79400),
),
);
创建ColorScheme只需要给对应的属性填上不同的色值即可。
代码语言:javascript复制ColorScheme colorScheme = ColorScheme(
brightness: isDark ? Brightness.dark : Brightness.light,
primary: accent1,
onPrimary: Colors.white,
secondary: accent1,
onSecondary: Colors.white,
error: Colors.red.shade400,
onError: Colors.red.shade400,
background: bg1,
onBackground: textColor,
surface: bg1,
onSurface: textColor,
);
❝新版本的Flutter,还提供了fromSeed方法,让开发者可以根据一个基色来生成符合Material Design规范的ColorScheme。 ❞
MaterialDesign提供了ThemeBuilder来帮助开发者创建这些代码。
https://material-foundation.github.io/material-theme-builder/#/custom
下面这张图,就展示了Flutter中不同的Color之间的关系。
本文原创公众号:群英传,授权转载请联系微信,授权后,请在原创发表24小时后转载。