爲了讓你的 App 更美觀,主題切換已是一個必不可少的功能了,但若是想在傳統的 Android 和 iOS 上分別適配不一樣的主題至關繁瑣。但這一切,在 Flutter 中都很是容易實現。今天咱們就來看看,如何在 Flutter 中給你的 App 添加換膚功能。咱們要實現的效果以下:html
在該案例中,我使用到了 provider
和 flustars
兩個庫,簡單介紹一下這兩個庫:微信
官方推薦的狀態管理庫,相比其餘狀態管理庫使用起來比較方便。app
狀態管理:通俗的講,當咱們想在多個頁面(組件/Widget)之間共享狀態(數據),或者一個頁面(組件/Widget)中的多個子組件之間共享狀態(數據),這個時候咱們就能夠用 Flutter 中的狀態管理來管理統一的狀態(數據),實現不一樣組件直接的傳值和數據共享。less
號稱「Flutter 全網最全經常使用工具類」,其中包括了SpUtil
、ScreenUtil
、TimelineUtil
等常見工具類,這裏咱們要使用的是SpUtil
這個部分,用於存儲用戶所選擇的主題信息。async
以上就是關於咱們使用的兩個第三方庫的介紹,若是想要使用,咱們須要在pubspec.yaml
文件中添加以下內容:ide
provider: ^4.0.5
flustars: ^0.2.6+1
複製代碼
準備工做作好了,接下來咱們就開始編碼吧。工具
咱們須要先想好本身所須要切換的主題樣式列表,若是以爲麻煩的能夠直接用下面的內容:字體
Map<String, Color> themeColorMap = {
'gray': Colors.grey,
'blue': Colors.blue,
'blueAccent': Colors.blueAccent,
'cyan': Colors.cyan,
'deepPurple': Colors.purple,
'deepPurpleAccent': Colors.deepPurpleAccent,
'deepOrange': Colors.orange,
'green': Colors.green,
'indigo': Colors.indigo,
'indigoAccent': Colors.indigoAccent,
'orange': Colors.orange,
'purple': Colors.purple,
'pink': Colors.pink,
'red': Colors.red,
'teal': Colors.teal,
'black': Colors.black,
};
複製代碼
而後咱們就須要使用 Provider 來進行全局的狀態管理了。首先先建立一個app_provider.dart
文件,而後添加以下代碼:ui
class AppInfoProvider with ChangeNotifier {
String _themeColor = '';
String get themeColor => _themeColor;
setTheme(String themeColor) {
_themeColor = themeColor;
notifyListeners();
}
}
複製代碼
由於是全局的狀態管理,接下來咱們須要在main.dart
文件中配置一下剛纔建立的 provider,有多個狀態管理就使用 MultiProvider,單個的使用 Provider.value 就好了。(考慮到將來項目的擴展,這裏我就直接使用 MultiProvider)了編碼
class MyApp extends StatelessWidget {
Color _themeColor;
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [ChangeNotifierProvider.value(value: AppInfoProvider())],
child: Consumer<AppInfoProvider>(
builder: (context, appInfo, _) {
String colorKey = appInfo.themeColor;
if (themeColorMap[colorKey] != null) {
_themeColor = themeColorMap[colorKey];
}
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primaryColor: _themeColor,
floatingActionButtonTheme:
FloatingActionButtonThemeData(backgroundColor: _themeColor),
),
home: MyHomePage(title: 'Flutter Theme Change demo'),
);
},
),
);
}
}
複製代碼
若是想要在某個地方改變主題,咱們只須要執行下面這行代碼便可。
Provider.of<AppInfoProvider>(context).setTheme(colorKey);
複製代碼
咱們先來講說上面這段代碼,重點就在於 ThemeData 的設置:
咱們看看ThemeData
部分數據定義:
ThemeData({
Brightness brightness, //深色仍是淺色
MaterialColor primarySwatch, //主題顏色樣本,見下面介紹
Color primaryColor, //主色,決定導航欄顏色
Color accentColor, //次級色,決定大多數Widget的顏色,如進度條、開關等。
Color cardColor, //卡片顏色
Color dividerColor, //分割線顏色
ButtonThemeData buttonTheme, //按鈕主題
Color cursorColor, //輸入框光標顏色
Color dialogBackgroundColor,//對話框背景顏色
String fontFamily, //文字字體
TextTheme textTheme,// 字體主題,包括標題、body等文字樣式
IconThemeData iconTheme, // Icon的默認樣式
TargetPlatform platform, //指定平臺,應用特定平臺控件風格
...
})
複製代碼
上面只是ThemeData
的一小部分屬性,完整的數據定義讀者能夠查看 SDK。
之因此使用
floatingActionButtonTheme
單獨設置floatingActionButton
而不是使用accentTextTheme
,是由於會有警告 ⚠️The support for configuring the foreground color of FloatingActionButtons using ThemeData.accentIconTheme has been deprecated. Please use ThemeData.floatingActionButtonTheme instead. See https://flutter.dev/go/remove-fab-accent-theme-dependency. This feature was deprecated after v1.13.2.
意思就是這個屬性將會在1.13.2
中被廢棄。不過並不影響咱們如今的使用。
更多關於主題的內容能夠參考 👉顏色和主題
這裏就須要使用到一開始提到的flustars
中的SpUtil
了,咱們通常會在頁面初始化加載的時候讀取保存的顏色信息,因此咱們須要在初始化頁面配置以下代碼:
String _colorKey;
@override
void initState() {
super.initState();
_initAsync();
}
Future<void> _initAsync() async {
await SpUtil.getInstance();
_colorKey = SpUtil.getString('key_theme_color', defValue: 'blue');
// 設置初始化主題顏色
Provider.of<AppInfoProvider>(context, listen: false).setTheme(_colorKey);
}
複製代碼
await SpUtil.getInstance();
這段代碼用於加載SpUtil
庫,經過看源碼咱們知道,這個庫採用了單例模式,固然,這不是這篇文章的重點。
上面這段代碼用於初始化主題,咱們經過SpUtil.getString('key_theme_color', defValue: 'blue');
獲取保存的主題信息,而後再使用Provider.of<AppInfoProvider>(context, listen: false).setTheme(_colorKey);
設置主題便可。
初始化主題弄好了,那選擇的代碼又如何編寫呢?
很簡單,只須要才合適的地方調用下面的代碼就能夠了。
setState(() {
_colorKey = key;
});
SpHelper.putString('key_theme_color', key);
Provider.of<AppInfoProvider>(context).setTheme(key);
複製代碼
思路和上面大同小異,無非是將getString
換成了putString
。
上面的代碼提供了切換主題的思路,可是對於用戶來講,他們所要作的是有一個界面可讓他們直接切換主題,所以,下面咱們來編寫切換主題的控件。
由於切換主題一般會在設置界面中出現,因此這裏我用了一個ExpansionTile
,這是一個能夠展開的ListTile
,代碼以下:
…………
ExpansionTile(
leading: Icon(Icons.color_lens),
title: Text('顏色主題'),
initiallyExpanded: false,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 10, right: 10, bottom: 10),
child: Wrap(
spacing: 8,
runSpacing: 8,
children: themeColorMap.keys.map((key) {
Color value = themeColorMap[key];
return InkWell(
onTap: () {
setState(() {
_colorKey = key;
});
SpUtil.putString('key_theme_color', key);
Provider.of<AppInfoProvider>(context, listen: false)
.setTheme(key);
},
child: Container(
width: 40,
height: 40,
color: value,
child: _colorKey == key
? Icon(
Icons.done,
color: Colors.white,
)
: null,
),
);
}).toList(),
),
)
],
),
…………
複製代碼
效果以下:
上面這段代碼就是將咱們最開始選定的一些主題themeColorMap
展現出來,告訴用戶能夠切換哪些主題。其中onTap
內的代碼就是上一節中提到的設置顏色主題的方法,InkWell
主要用於提供主題色的點擊效果,換成GestureDetector
也是能夠的。
至此咱們的換膚功能也就完成了,想要獲取完整代碼的能夠關注公衆號「01 二進制」,後臺回覆「Flutter 主題切換」。
以上就是關於如何在 Flutter 中切換主題的詳細內容了。能夠看出,相較於原生應用主題的適配,在 Flutter 中實現換膚的功能簡單不少了。
最後來發布一篇預告,由於 iOS 13 和 Android 10 系統上都新增了「深色模式」,在文中我也提到了ThemeData
的Brightness brightness
屬性用於表示深色仍是淺色。下一篇文章我就來聊一聊深色模式的適配。
若是你以爲個人文章對你有所幫助,不妨給個贊 👍 或者關注支持一下。