Flutter深色模式適配的實現

一、簡介

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/flutter_wanandroid

到此這篇關於Flutter深色模式適配的實現的文章就介紹到這瞭,更多相關Flutter深色模式適配內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!