一、簡介
Flutter的深色模式以及跟隨系統設定比較簡單,我感覺需要注意的是開發過程中儘量使用Theme中的顏色與樣式,開發過程中遇到的比較大的坑就是provider
的一些問題,可能是因為我用的版本新一些,網上找了很多文章,總會遇到一些問題。本文的深色模式適配是通過修改themeMode
來實現的,供諸位有緣人蔘考。
二、環境介紹
1. Flutter: 2.0.3
2. Dart: 2.12.0
3. provider: 5.0.0
狀態管理,用於執行時切換主題
4. shared_preferences: 2.0.5
資料持久化,用於儲存當前選中的主題,以便下次啟動時讀取使用使用者選擇的主題
environment:
sdk: ">=2.12.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
# 忽略了一些依賴...
# shared_preferences https://pub.flutter-io.cn/packages/shared_preferences
shared_preferences: ^2.0.5
# 全域性狀態管理 https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md
provider: ^5.0.0
複製程式碼
三、主題
1. ThemeData
factory ThemeData({
Brightness brightness, // 應用主題亮度,可選(dark、light)
VisualDensity visualDensity, // 視覺密度
MaterialColor primarySwatch, // 主要樣式,設定primaryColor後該背景色會被覆蓋
Color primaryColor, // 主要部分背景顏色(導航和tabBar等)
Brightness primaryColorBrightness, // primaryColor的亮度
Color primaryColorLight, // primaryColor的淺色版
Color primaryColorDark, // primaryColor的深色版
Color accentColor, // 前景色(文字,按鈕等)
Brightness accentColorBrightness, // accentColor的亮度
Color canvasColor, // MaterialType.canvas 的預設顏色
Color shadowColor, // 陰影顏色
Color scaffoldBackgroundColor, // Scaffold的背景顏色。典型Material應用程式或應用程式內頁面的背景顏色
Color bottomAppBarColor, // BottomAppBar的預設顏色
Color cardColor, // Card的顏色
Color dividerColor, // Divider和PopupMenuDivider的顏色,也用於ListTile之間、DataTable的行之間等。
Color focusColor, // 突出顏色
Color hoverColor, // hoverColor
Color highlightColor, // 高亮顏色,選中在潑墨動畫期間使用的突出顯示顏色,或用於指示選單中的項。
Color splashColor, // 墨水飛濺的顏色。InkWell
InteractiveInkFeatureFactory splashFactory, // 定義由InkWell和InkResponse反應產生的墨濺的外觀。
Color selectedRowColor, // 用於突出顯示選定行的顏色。
Color unselectedWidgetColor, // 用於處於非活動(但已啟用)狀態的小部件的顏色。例如,未選中的核取方塊。通常與accentColor形成對比。也看到disabledColor。
Color disabledColor, // 禁用狀態下部件的顏色,無論其當前狀態如何。例如,一個禁用的核取方塊(可以選中或未選中)。
Color buttonColor, // RaisedButton按鈕中使用的Material 的預設填充顏色。
ButtonThemeData buttonTheme, // 定義按鈕部件的預設配置,
ToggleButtonsThemeData toggleButtonsTheme, // 切換按鈕的主題
Color secondaryHeaderColor, // 選定行時PaginatedDataTable標題的顏色。
Color textSelectionColor, // 文字框中文字選擇的顏色,如TextField
Color cursorColor, // 文字框中游標的顏色,如TextField
Color textSelectionHandleColor, // 調整當前選定的文字部分的控制程式碼的顏色。
Color backgroundColor, // 與主色形成對比的顏色,例如用作進度條的剩餘部分。
Color dialogBackgroundColor, // Dialog元素的背景顏色
Color indicatorColor, // 選項卡中選定的選項卡指示器的顏色。
Color hintColor, // 用於提示文字或佔位符文字的顏色,例如在TextField中。
Color errorColor, // 用於輸入驗證錯誤的顏色,例如在TextField中
Color toggleableActiveColor, // 用於突出顯示Switch、Radio和Checkbox等可切換小部件的活動狀態的顏色。
String fontFamily, // 文字字型
TextTheme textTheme, // 文字的顏色與卡片和畫布的顏色形成對比。
TextTheme primaryTextTheme, // 與primaryColor形成對比的文字主題
TextTheme accentTextTheme, // 與accentColor形成對比的文字主題。
InputDecorationTheme inputDecorationTheme, // 基於這個主題的 InputDecorator、TextField和TextFormField的預設InputDecoration值。
TabBarTheme tabBarTheme, // 用於自定義選項卡欄指示器的大小、形狀和顏色的主題。
TooltipThemeData tooltipTheme, // tooltip主題
CardTheme cardTheme, // Card的顏色和樣式
AppBarTheme appBarTheme, // appBar主題
ColorScheme colorScheme, // 擁有13種顏色,可用於配置大多陣列件的顏色。
NavigationRailThemeData navigationRailTheme, // 導航邊欄主題
// ...
})
複製程式碼
2. main.dart or MaterialApp
theme
為預設主題,darkTheme
為深色主題,themeMode
為當前使用哪個主題,可選值system
、light
、dark
,只有在th``eme
與darkTheme
都設定的時候才會生效,我們的theme
與darkTheme
都直接使用ThemeData
物件,給他指定了brightness
,而不是使用這樣感覺可以方便修改樣式,當然也可以抽出來封裝一下,我這沒有去處理。
MaterialApp(
theme: ThemeData(
brightness: Brightness.light,
// scaffoldBackgroundColor: Color(0xFFF5F5F9),
),
darkTheme: ThemeData(
brightness: Brightness.dark,
// scaffoldBackgroundColor: Color(0xFFF5F5F9),
),
themeMode: context.watch<ThemeModel>().theme
);
複製程式碼
四、全域性配置
全域性配置是在MaterialApp
載入之前進行一寫初始化操作,參考了《Flutter實戰》電子書,Flutter當中SharedPreferences
是非同步初始化,還有Dio
網路請求的快取也需要提前初始化,我們這裡SharedPreferences
載入完之後在進行之後的操作,SpUtils
中的SharedPreferences
使用的Global
全域性配置中的靜態屬性。
1. Global
class Global {
static late SharedPreferences prefs;
static ThemeMode theme = ThemeMode.light;
// 是否為release版
static bool get isRelease => bool.fromEnvironment("dart.vm.product");
//初始化全域性資訊,會在APP啟動時執行
static Future init() async {
WidgetsFlutterBinding.ensureInitialized();
prefs = await SharedPreferences.getInstance();
// 當前本地儲存的主題
String themeValue = await SpUtils.instance.getStorage(SpConstants.skin);
theme = themeStringToThemeMode(themeValue);
//初始化網路請求相關配置
HttpManager();
}
}
複製程式碼
2. main.dart
// Global載入完成後掉用runApp
Global.init().then((e) => runApp());
複製程式碼
3. themeStringToThemeMode()
字串轉ThemeMode
ThemeMode themeStringToThemeMode(String themeValue){
ThemeMode theme = ThemeMode.light;
switch (themeValue) {
case "light":
theme = ThemeMode.light;
break;
case "dark":
theme = ThemeMode.dark;
break;
case "system":
theme = ThemeMode.system;
break;
}
return theme;
}
複製程式碼
五、使用狀態管理(provider)切換主題
> 此處大坑,處處勸退,感謝Flutter provider勸退經歷這篇文章
1. 構建主題Model
class ThemeModel extends ChangeNotifier {
// 獲取當前主題,如果為設定主題,則預設使用淺色模式
ThemeMode get theme => Global.theme;
// 主題改變後,通知其依賴項,新主題會立即生效
set theme(ThemeMode themeMode) {
if (themeMode != theme) {
Global.theme = themeMode;
notifyListeners();
}
}
}
複製程式碼
2. main.dart(監聽值變化)
此處為main.dart
檔案的完整程式碼,下面有關provider
的一些使用方式可能與網上很多文章不一樣的,但是這都是官網文件的最新推薦使用方式。讀取當前provider
中儲存的主題context.watch<ThemeModel>().theme
void main() {
//頂部狀態列透明
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(statusBarColor: Colors.transparent));
Global.init().then((e) => runApp(
MultiProvider(
providers: [ListenableProvider<ThemeModel>(create: (_) => ThemeModel())],
builder: (context, child) {
return WanAndroid();
}),
));
}
class WanAndroid extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
theme: ThemeData(
brightness: Brightness.light,
// scaffoldBackgroundColor: Color(0xFFF5F5F9),
),
darkTheme: ThemeData(
brightness: Brightness.dark,
// scaffoldBackgroundColor: Color(0xFFF5F5F9),
),
themeMode: context.watch<ThemeModel>().theme,
routes: {
'/': (context) => SplashPage(),
'/index': (context) => IndexPage(),
'/login': (context) => LoginPage(),
'/setting': (context) => SettingPage(),
},
title: '玩Android-Flutter版',
);
}
}
複製程式碼
3. 切換主題
修改provider
中儲存的值即可。
// themeStringToThemeMode方法程式碼在上面有寫
context.read<ThemeModel>().theme = themeStringToThemeMode(value);
複製程式碼
六、原始碼
- 原始碼:github.com/sdwfqin/flu…