W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
在介紹主題前我們先了解一些 Flutter 中的 Color 類。Color 類中顏色以一個(gè)int值保存,我們知道顯示器顏色是由紅、綠、藍(lán)三基色組成,每種顏色占8比特,存儲(chǔ)結(jié)構(gòu)如下:
Bit(位) | 顏色 |
---|---|
0-7 | 藍(lán)色 |
8-15 | 綠色 |
16-23 | 紅色 |
24-31 | Alpha (不透明度) |
上面表格中的的字段在 Color 類中都有對應(yīng)的屬性,而 Color 中的眾多方法也就是操作這些屬性的,由于大多比較簡單,讀者可以查看類定義了解。在此我們主要討論兩點(diǎn):色值轉(zhuǎn)換和亮度。
如 Web 開發(fā)中的色值通常是一個(gè)字符串如"#dc380d",它是一個(gè) RGB 值,我們可以通過下面這些方法將其轉(zhuǎn)為 Color 類:
Color(0xffdc380d); //如果顏色固定可以直接使用整數(shù)值
//顏色是一個(gè)字符串變量
var c = "dc380d";
Color(int.parse(c,radix:16)|0xFF000000) //通過位運(yùn)算符將Alpha設(shè)置為FF
Color(int.parse(c,radix:16)).withAlpha(255) //通過方法將Alpha設(shè)置為FF
假如,我們要實(shí)現(xiàn)一個(gè)背景顏色和 Title 可以自定義的導(dǎo)航欄,并且背景色為深色時(shí)我們應(yīng)該讓 Title 顯示為淺色;背景色為淺色時(shí),Title 顯示為深色。要實(shí)現(xiàn)這個(gè)功能,我們就需要來計(jì)算背景色的亮度,然后動(dòng)態(tài)來確定 Title 的顏色。Color 類中提供了一個(gè)computeLuminance()
方法,它可以返回一個(gè)[0-1]的一個(gè)值,數(shù)字越大顏色就越淺,我們可以根據(jù)它來動(dòng)態(tài)確定Title的顏色,下面是導(dǎo)航欄 NavBar 的簡單實(shí)現(xiàn):
class NavBar extends StatelessWidget {
final String title;
final Color color; //背景顏色
NavBar({
Key key,
this.color,
this.title,
});
@override
Widget build(BuildContext context) {
return Container(
constraints: BoxConstraints(
minHeight: 52,
minWidth: double.infinity,
),
decoration: BoxDecoration(
color: color,
boxShadow: [
//陰影
BoxShadow(
color: Colors.black26,
offset: Offset(0, 3),
blurRadius: 3,
),
],
),
child: Text(
title,
style: TextStyle(
fontWeight: FontWeight.bold,
//根據(jù)背景色亮度來確定Title顏色
color: color.computeLuminance() < 0.5 ? Colors.white : Colors.black,
),
),
alignment: Alignment.center,
);
}
}
測試代碼如下:
Column(
children: <Widget>[
//背景為藍(lán)色,則title自動(dòng)為白色
NavBar(color: Colors.blue, title: "標(biāo)題"),
//背景為白色,則title自動(dòng)為黑色
NavBar(color: Colors.white, title: "標(biāo)題"),
]
)
運(yùn)行效果如圖7-4所示:
MaterialColor
是實(shí)現(xiàn) Material Design 中的顏色的類,它包含一種顏色的10個(gè)級(jí)別的漸變色。MaterialColor
通過"[]"運(yùn)算符的索引值來代表顏色的深度,有效的索引有:50,100,200,…,900,數(shù)字越大,顏色越深。MaterialColor
的默認(rèn)值為索引等于500的顏色。舉個(gè)例子,Colors.blue
是預(yù)定義的一個(gè)MaterialColor
類對象,定義如下:
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;
Colors.blue[50]
到Colors.blue[900]
的色值從淺藍(lán)到深藍(lán)漸變,效果如圖7-5所示:
Theme
組件可以為 Material APP 定義主題數(shù)據(jù)(ThemeData)。Material 組件庫里很多組件都使用了主題數(shù)據(jù),如導(dǎo)航欄顏色、標(biāo)題字體、Icon 樣式等。Theme
內(nèi)會(huì)使用InheritedWidget
來為其子樹共享樣式數(shù)據(jù)。
ThemeData
用于保存是 Material 組件庫的主題數(shù)據(jù),Material 組件需要遵守相應(yīng)的設(shè)計(jì)規(guī)范,而這些規(guī)范可自定義部分都定義在 ThemeData 中了,所以我們可以通過 ThemeData 來自定義應(yīng)用主題。在子組件中,我們可以通過Theme.of
方法來獲取當(dāng)前的ThemeData
。
注意:Material Design 設(shè)計(jì)規(guī)范中有些是不能自定義的,如導(dǎo)航欄高度,ThemeData 只包含了可自定義部分。
我們看看ThemeData
部分?jǐn)?shù)據(jù)定義:
ThemeData({
Brightness brightness, //深色還是淺色
MaterialColor primarySwatch, //主題顏色樣本,見下面介紹
Color primaryColor, //主色,決定導(dǎo)航欄顏色
Color accentColor, //次級(jí)色,決定大多數(shù)Widget的顏色,如進(jìn)度條、開關(guān)等。
Color cardColor, //卡片顏色
Color dividerColor, //分割線顏色
ButtonThemeData buttonTheme, //按鈕主題
Color cursorColor, //輸入框光標(biāo)顏色
Color dialogBackgroundColor,//對話框背景顏色
String fontFamily, //文字字體
TextTheme textTheme,// 字體主題,包括標(biāo)題、body等文字樣式
IconThemeData iconTheme, // Icon的默認(rèn)樣式
TargetPlatform platform, //指定平臺(tái),應(yīng)用特定平臺(tái)控件風(fēng)格
...
})
上面只是ThemeData
的一小部分屬性,完整的數(shù)據(jù)定義讀者可以查看 SDK。上面屬性中需要說明的是primarySwatch
,它是主題顏色的一個(gè)"樣本色",通過這個(gè)樣本色可以在一些條件下生成一些其它的屬性,例如,如果沒有指定primaryColor
,并且當(dāng)前主題不是深色主題,那么primaryColor
就會(huì)默認(rèn)為primarySwatch
指定的顏色,還有一些相似的屬性如accentColor
、indicatorColor
等也會(huì)受primarySwatch
影響。
我們實(shí)現(xiàn)一個(gè)路由換膚功能:
class ThemeTestRoute extends StatefulWidget {
@override
_ThemeTestRouteState createState() => new _ThemeTestRouteState();
}
class _ThemeTestRouteState extends State<ThemeTestRoute> {
Color _themeColor = Colors.teal; //當(dāng)前路由主題色
@override
Widget build(BuildContext context) {
ThemeData themeData = Theme.of(context);
return Theme(
data: ThemeData(
primarySwatch: _themeColor, //用于導(dǎo)航欄、FloatingActionButton的背景色等
iconTheme: IconThemeData(color: _themeColor) //用于Icon顏色
),
child: Scaffold(
appBar: AppBar(title: Text("主題測試")),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
//第一行Icon使用主題中的iconTheme
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.favorite),
Icon(Icons.airport_shuttle),
Text(" 顏色跟隨主題")
]
),
//為第二行Icon自定義顏色(固定為黑色)
Theme(
data: themeData.copyWith(
iconTheme: themeData.iconTheme.copyWith(
color: Colors.black
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.favorite),
Icon(Icons.airport_shuttle),
Text(" 顏色固定黑色")
]
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () => //切換主題
setState(() =>
_themeColor =
_themeColor == Colors.teal ? Colors.blue : Colors.teal
),
child: Icon(Icons.palette)
),
),
);
}
}
運(yùn)行后點(diǎn)擊右下角懸浮按鈕則可以切換主題,如圖7-6、7-7所示:
需要注意的有三點(diǎn):
Theme.of(BuildContext context)
來獲取的,我們看看其簡化后的代碼:static ThemeData of(BuildContext context, { bool shadowThemeOnly = false }) {
// 簡化代碼,并非源碼
return context.dependOnInheritedWidgetOfExactType<_InheritedTheme>().theme.data
}
context.dependOnInheritedWidgetOfExactType
會(huì)在 widget 樹中從當(dāng)前位置向上查找第一個(gè)類型為_InheritedTheme
的 widget。所以當(dāng)局部指定Theme
后,其子樹中通過Theme.of()
向上查找到的第一個(gè)_InheritedTheme
便是我們指定的Theme
。
MaterialApp
的theme
屬性。Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: