FlutterComponent最佳實(shí)踐之色彩管理
點(diǎn)擊上方藍(lán)字關(guān)注我,知識(shí)會(huì)給你力量
Flutter中關(guān)于色彩和主題的內(nèi)容非常之多,我們需要理清不同的Color之間的異同,才能更好的開(kāi)發(fā)Flutter應(yīng)用。
MaterialColor
在ThemeData的構(gòu)造函數(shù)中,我們可以發(fā)現(xiàn)兩個(gè)很有意思的屬性
MaterialColor? primarySwatch,
Color? primaryColor,
在Flutter創(chuàng)建的Demo中,Theme是這樣設(shè)置的。
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
有沒(méi)有人很好奇,primarySwatch和primaryColor,到底要設(shè)置哪個(gè)?到底誰(shuí)才是真正的「主色調(diào)」?
首先,MaterialColor并不等于Color,它是基于MaterialDesign而產(chǎn)生的一套顏色體系。
在這個(gè)顏色系統(tǒng)中,基色和明暗不同的10種顏色作為一組處理,從而形成了MaterialColor。
前面代碼中的Colors.blue,實(shí)際上就是一個(gè)MaterialColor,我們來(lái)看下它的實(shí)現(xiàn)。
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;
由此可見(jiàn),MaterialColor的10種顏色是怎么實(shí)現(xiàn)的。
事實(shí)上,MaterialColor的定義就是如此,一個(gè)基色,加上一個(gè)不同shade的Map。
如果你要自定義一個(gè)MaterialColor,那么這10種色調(diào),也是必須要實(shí)現(xiàn)的。
?除了MaterialColor以外,還有一個(gè)MaterialAccentColor,它和MaterialColor類(lèi)似,但是只有5種色調(diào)。
?
Color
Colors:這個(gè)類(lèi)是來(lái)自Material調(diào)色板的顏色。要在代碼中訪問(wèn)它們,只需調(diào)用基色和shade值即可。
color: Colors.red
color: Colors.red[200]
Color:你可以將這個(gè)類(lèi)用于Material調(diào)色板以外的顏色,因?yàn)樗试SARGB(Alpha, Red, Green, Blue)格式的顏色值。最常見(jiàn)的使用方法是像下面的代碼這樣傳遞十六進(jìn)制顏色代碼,其中0xFF代表完全不透明的顏色。
Color(0xFF42A5F5)
primarySwatch
接下來(lái),我們繼續(xù)來(lái)看前面提到的那個(gè)問(wèn)題,為什么ThemeData中需要設(shè)置primarySwatch。
在theme_data的源代碼中,我們可以發(fā)現(xiàn)這樣的代碼。
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中設(shè)不設(shè)置primarySwatch都會(huì)是藍(lán)色的主題色的原因了。
那么一個(gè)具體的Flutter組件,是如何決定自己的主題的呢?以Appbar為例,我們?cè)谠创a中找到對(duì)應(yīng)設(shè)置background的地方。
final Color backgroundColor = backwardsCompatibility
? widget.backgroundColor
?? appBarTheme.backgroundColor
?? theme.primaryColor
: _resolveColor(
states,
widget.backgroundColor,
appBarTheme.backgroundColor,
colorScheme.brightness == Brightness.dark ? colorScheme.surface : colorScheme.primary,
);
?不要被這里茫茫多的問(wèn)號(hào)搞昏了,復(fù)習(xí)一下Dart語(yǔ)法吧。
?
「?.」——代表非空訪問(wèn),例如「myObject?.someProperty」,等價(jià)于——「(myObject != null) ? myObject.someProperty : null」 「??」——代表避空判斷,例如「a ?? 3」a為空時(shí),返回3 「??=」——同樣是避空賦值,例如「a ??= 3」a為空時(shí),a賦值為3
了解了這些之后,你應(yīng)該就能看懂上面的代碼了,原來(lái)Appbar的background是經(jīng)過(guò)很多場(chǎng)景來(lái)判斷的,簡(jiǎn)而言之:
-
先判斷是否在Appbar中設(shè)置了backgroundColor -
再判斷是否指定了AppBarTheme.backgroundColor,也就是針對(duì)Appbar進(jìn)行的Theme覆蓋 -
最后再根據(jù)是否黑夜模式來(lái)判斷使用ColorScheme.primary還是ColorScheme.surface
大部分的Flutter組件,幾乎都遵循這個(gè)判斷流程,只是使用的Color類(lèi)型不太一樣。
但是,primaryColor并不是沒(méi)用了,它可以用來(lái)更改組件的Theme,用于局部主題的使用。
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
色彩的千變?nèi)f化,最終會(huì)導(dǎo)致Theme屬性的膨脹,這是可以預(yù)見(jiàn)的,所以你可以看看ThemeData有多少屬性需要配置就知道了。
在ThemeData的構(gòu)造函數(shù)中,有超過(guò)70種的Color和Theme,這要全部通過(guò)手工來(lái)配置,將是一個(gè)非常大的工作量。
因此,F(xiàn)lutter引入了ColorScheme屬性,它是一組基于Material規(guī)范的25種顏色(9種必選色),可用于配置大多數(shù)組件的顏色屬性。Flutter團(tuán)隊(duì)計(jì)劃用定義好的ColorScheme來(lái)設(shè)計(jì)材質(zhì)組件的樣式。要使用colorScheme,你必須調(diào)用ThemeData.from()構(gòu)造函數(shù)。
ThemeData.from(
colorScheme: const ColorScheme.light().copyWith(
primary: const Color(0xff455a64),
primaryContainer: const Color(0xff1c313a),
secondary: const Color(0xffffc400),
secondaryContainer: const Color(0xffc79400),
),
);
創(chuàng)建ColorScheme只需要給對(duì)應(yīng)的屬性填上不同的色值即可。
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方法,讓開(kāi)發(fā)者可以根據(jù)一個(gè)基色來(lái)生成符合Material Design規(guī)范的ColorScheme。
?
MaterialDesign提供了ThemeBuilder來(lái)幫助開(kāi)發(fā)者創(chuàng)建這些代碼。
https://material-foundation.github.io/material-theme-builder/#/custom
下面這張圖,就展示了Flutter中不同的Color之間的關(guān)系。
向大家推薦下我的網(wǎng)站 https://xuyisheng.top/ 點(diǎn)擊原文一鍵直達(dá)
專(zhuān)注 Android-Kotlin-Flutter 歡迎大家訪問(wèn)
往期推薦
更文不易,點(diǎn)個(gè)“三連”支持一下??
