♿ Make theme switcher easier to use
This commit is contained in:
		| @@ -5,11 +5,12 @@ import 'package:get/get.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:media_kit/media_kit.dart'; | ||||
| import 'package:protocol_handler/protocol_handler.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:sentry_flutter/sentry_flutter.dart'; | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
| import 'package:solian/bootstrapper.dart'; | ||||
| import 'package:solian/firebase_options.dart'; | ||||
| import 'package:solian/platform.dart'; | ||||
| import 'package:solian/providers/theme_switcher.dart'; | ||||
| import 'package:solian/providers/websocket.dart'; | ||||
| import 'package:solian/providers/auth.dart'; | ||||
| import 'package:solian/providers/content/attachment.dart'; | ||||
| @@ -38,7 +39,6 @@ void main() async { | ||||
|       MediaKit.ensureInitialized(); | ||||
|  | ||||
|       await Future.wait([ | ||||
|         _initializeTheme(), | ||||
|         _initializeFirebase(), | ||||
|         _initializePlatformComponents(), | ||||
|       ]); | ||||
| @@ -51,16 +51,6 @@ void main() async { | ||||
|   ); | ||||
| } | ||||
|  | ||||
| Future<void> _initializeTheme() async { | ||||
|   final prefs = await SharedPreferences.getInstance(); | ||||
|   if (prefs.containsKey('global_theme_color')) { | ||||
|     final value = prefs.getInt('global_theme_color')!; | ||||
|     final color = Color(value); | ||||
|     currentLightTheme = SolianTheme.build(Brightness.light, seedColor: color); | ||||
|     currentDarkTheme = SolianTheme.build(Brightness.dark, seedColor: color); | ||||
|   } | ||||
| } | ||||
|  | ||||
| Future<void> _initializeFirebase() async { | ||||
|   await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); | ||||
| } | ||||
| @@ -83,33 +73,45 @@ Future<void> _initializePlatformComponents() async { | ||||
|   } | ||||
| } | ||||
|  | ||||
| final themeSwitcher = ThemeSwitcher( | ||||
|   lightThemeData: SolianTheme.build(Brightness.light), | ||||
|   darkThemeData: SolianTheme.build(Brightness.dark), | ||||
| )..restoreTheme(); | ||||
|  | ||||
| class SolianApp extends StatelessWidget { | ||||
|   const SolianApp({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return GetMaterialApp.router( | ||||
|       title: 'Solian', | ||||
|       theme: currentLightTheme, | ||||
|       darkTheme: currentDarkTheme, | ||||
|       themeMode: ThemeMode.system, | ||||
|       routerDelegate: AppRouter.instance.routerDelegate, | ||||
|       routeInformationParser: AppRouter.instance.routeInformationParser, | ||||
|       routeInformationProvider: AppRouter.instance.routeInformationProvider, | ||||
|       backButtonDispatcher: AppRouter.instance.backButtonDispatcher, | ||||
|       translations: SolianMessages(), | ||||
|       locale: Get.deviceLocale, | ||||
|       fallbackLocale: const Locale('en', 'US'), | ||||
|       onInit: () => _initializeProviders(context), | ||||
|       builder: (context, child) { | ||||
|         return SystemShell( | ||||
|           child: ScaffoldMessenger( | ||||
|             child: BootstrapperShell( | ||||
|               child: child ?? const SizedBox(), | ||||
|             ), | ||||
|           ), | ||||
|     return ChangeNotifierProvider.value( | ||||
|       value: themeSwitcher, | ||||
|       child: Builder(builder: (context) { | ||||
|         final theme = Provider.of<ThemeSwitcher>(context); | ||||
|  | ||||
|         return GetMaterialApp.router( | ||||
|           title: 'Solian', | ||||
|           theme: theme.lightThemeData, | ||||
|           darkTheme: theme.darkThemeData, | ||||
|           themeMode: ThemeMode.system, | ||||
|           routerDelegate: AppRouter.instance.routerDelegate, | ||||
|           routeInformationParser: AppRouter.instance.routeInformationParser, | ||||
|           routeInformationProvider: AppRouter.instance.routeInformationProvider, | ||||
|           backButtonDispatcher: AppRouter.instance.backButtonDispatcher, | ||||
|           translations: SolianMessages(), | ||||
|           locale: Get.deviceLocale, | ||||
|           fallbackLocale: const Locale('en', 'US'), | ||||
|           onInit: () => _initializeProviders(context), | ||||
|           builder: (context, child) { | ||||
|             return SystemShell( | ||||
|               child: ScaffoldMessenger( | ||||
|                 child: BootstrapperShell( | ||||
|                   child: child ?? const SizedBox(), | ||||
|                 ), | ||||
|               ), | ||||
|             ); | ||||
|           }, | ||||
|         ); | ||||
|       }, | ||||
|       }), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   | ||||
							
								
								
									
										30
									
								
								lib/providers/theme_switcher.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								lib/providers/theme_switcher.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
| import 'package:solian/theme.dart'; | ||||
|  | ||||
| class ThemeSwitcher extends ChangeNotifier { | ||||
|   ThemeData lightThemeData; | ||||
|   ThemeData darkThemeData; | ||||
|  | ||||
|   ThemeSwitcher({ | ||||
|     required this.lightThemeData, | ||||
|     required this.darkThemeData, | ||||
|   }); | ||||
|  | ||||
|   Future<void> restoreTheme() async { | ||||
|     final prefs = await SharedPreferences.getInstance(); | ||||
|     if (prefs.containsKey('global_theme_color')) { | ||||
|       final value = prefs.getInt('global_theme_color')!; | ||||
|       final color = Color(value); | ||||
|       lightThemeData = SolianTheme.build(Brightness.light, seedColor: color); | ||||
|       darkThemeData = SolianTheme.build(Brightness.dark, seedColor: color); | ||||
|       notifyListeners(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void setTheme(ThemeData light, dark) { | ||||
|     lightThemeData = light; | ||||
|     darkThemeData = dark; | ||||
|     notifyListeners(); | ||||
|   } | ||||
| } | ||||
| @@ -2,8 +2,10 @@ import 'dart:ui'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:get/get.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
| import 'package:solian/exts.dart'; | ||||
| import 'package:solian/providers/theme_switcher.dart'; | ||||
| import 'package:solian/theme.dart'; | ||||
|  | ||||
| class SettingScreen extends StatefulWidget { | ||||
| @@ -30,22 +32,16 @@ class _SettingScreenState extends State<SettingScreen> { | ||||
|       icon: Icon(Icons.circle, color: color), | ||||
|       tooltip: label, | ||||
|       onPressed: () { | ||||
|         currentLightTheme = SolianTheme.build( | ||||
|           Brightness.light, | ||||
|           seedColor: color, | ||||
|         ); | ||||
|         currentDarkTheme = SolianTheme.build( | ||||
|           Brightness.dark, | ||||
|           seedColor: color, | ||||
|         ); | ||||
|         if (!Get.isDarkMode) { | ||||
|           Get.changeTheme( | ||||
|             SolianTheme.build(Brightness.light, seedColor: color), | ||||
|           ); | ||||
|         } else { | ||||
|           // Dark mode cannot be hot reload | ||||
|           // https://github.com/jonataslaw/getx/issues/1411 | ||||
|         } | ||||
|         context.read<ThemeSwitcher>().setTheme( | ||||
|               SolianTheme.build( | ||||
|                 Brightness.light, | ||||
|                 seedColor: color, | ||||
|               ), | ||||
|               SolianTheme.build( | ||||
|                 Brightness.dark, | ||||
|                 seedColor: color, | ||||
|               ), | ||||
|             ); | ||||
|         _prefs.setInt('global_theme_color', color.value); | ||||
|         context.clearSnackbar(); | ||||
|         context.showSnackbar('themeColorApplied'.tr); | ||||
|   | ||||
| @@ -1,9 +1,6 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:solian/platform.dart'; | ||||
|  | ||||
| ThemeData? currentLightTheme = SolianTheme.build(Brightness.light); | ||||
| ThemeData? currentDarkTheme = SolianTheme.build(Brightness.dark); | ||||
|  | ||||
| abstract class SolianTheme { | ||||
|   static bool isLargeScreen(BuildContext context) => | ||||
|       MediaQuery.of(context).size.width > 640; | ||||
|   | ||||
| @@ -312,6 +312,5 @@ const i18nEnglish = { | ||||
|   'themeColorMiku': 'Miku Blue', | ||||
|   'themeColorKagamine': 'Kagamine Yellow', | ||||
|   'themeColorLuka': 'Luka Pink', | ||||
|   'themeColorApplied': | ||||
|       'Global theme color has been applied, dark mode theme need restart to get applied.', | ||||
|   'themeColorApplied': 'Global theme color has been applied.', | ||||
| }; | ||||
|   | ||||
| @@ -289,5 +289,5 @@ const i18nSimplifiedChinese = { | ||||
|   'themeColorMiku': '未来色', | ||||
|   'themeColorKagamine': '镜音黄', | ||||
|   'themeColorLuka': '流音粉', | ||||
|   'themeColorApplied': '全局主题颜色已应用,深色模式中主题需要重启生效', | ||||
|   'themeColorApplied': '全局主题颜色已应用', | ||||
| }; | ||||
|   | ||||
							
								
								
									
										16
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -1016,6 +1016,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.0.5" | ||||
|   nested: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: nested | ||||
|       sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.0.0" | ||||
|   nm: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -1272,6 +1280,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.2.0" | ||||
|   provider: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: provider | ||||
|       sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.1.2" | ||||
|   pub_semver: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|   | ||||
| @@ -2,7 +2,7 @@ name: solian | ||||
| description: "The Solar Network App" | ||||
| publish_to: "none" | ||||
|  | ||||
| version: 1.2.0+3 | ||||
| version: 1.2.0+4 | ||||
|  | ||||
| environment: | ||||
|   sdk: ">=3.3.4 <4.0.0" | ||||
| @@ -61,6 +61,7 @@ dependencies: | ||||
|   flutter_markdown_selectionarea: ^0.6.17+1 | ||||
|   shared_preferences: ^2.2.3 | ||||
|   easy_debounce: ^2.0.3 | ||||
|   provider: ^6.1.2 | ||||
|  | ||||
| dev_dependencies: | ||||
|   flutter_test: | ||||
|   | ||||
							
								
								
									
										107
									
								
								web/index.html
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								web/index.html
									
									
									
									
									
								
							| @@ -1,8 +1,7 @@ | ||||
| <!DOCTYPE html> | ||||
| <!doctype html> | ||||
| <html> | ||||
|  | ||||
| <head> | ||||
|   <!-- | ||||
|   <head> | ||||
|     <!-- | ||||
|     If you are serving your web app in a path other than the root, change the | ||||
|     href value below to reflect the base path you are serving from. | ||||
|  | ||||
| @@ -15,66 +14,64 @@ | ||||
|     This is a placeholder for base href that will be replaced by the value of | ||||
|     the `--base-href` argument provided to `flutter build`. | ||||
|   --> | ||||
|   <!-- <base href="$FLUTTER_BASE_HREF"> --> | ||||
|   <base href="/" /> | ||||
|     <base href="$FLUTTER_BASE_HREF" /> | ||||
|  | ||||
|   <meta charset="UTF-8"> | ||||
|   <meta content="IE=Edge" http-equiv="X-UA-Compatible"> | ||||
|   <meta name="description" content="A new Flutter project."> | ||||
|     <meta charset="UTF-8" /> | ||||
|     <meta content="IE=Edge" http-equiv="X-UA-Compatible" /> | ||||
|     <meta name="description" content="A new Flutter project." /> | ||||
|  | ||||
|   <!-- iOS meta tags & icons --> | ||||
|   <meta name="apple-mobile-web-app-capable" content="yes"> | ||||
|   <meta name="apple-mobile-web-app-status-bar-style" content="black"> | ||||
|   <meta name="apple-mobile-web-app-title" content="solian"> | ||||
|   <link rel="apple-touch-icon" href="icons/Icon-192.png"> | ||||
|     <!-- iOS meta tags & icons --> | ||||
|     <meta name="apple-mobile-web-app-capable" content="yes" /> | ||||
|     <meta name="apple-mobile-web-app-status-bar-style" content="black" /> | ||||
|     <meta name="apple-mobile-web-app-title" content="solian" /> | ||||
|     <link rel="apple-touch-icon" href="icons/Icon-192.png" /> | ||||
|  | ||||
|   <!-- Favicon --> | ||||
|   <link rel="icon" type="image/png" href="favicon.png" /> | ||||
|     <!-- Favicon --> | ||||
|     <link rel="icon" type="image/png" href="favicon.png" /> | ||||
|  | ||||
|   <!-- Loading styles --> | ||||
|   <style> | ||||
|     .loader-container { | ||||
|       display: flex; | ||||
|       justify-content: center; | ||||
|       align-items: center; | ||||
|       margin: 0; | ||||
|       position: absolute; | ||||
|       top: 50%; | ||||
|       left: 50%; | ||||
|       -ms-transform: translate(-50%, -50%); | ||||
|       transform: translate(-50%, -50%); | ||||
|     } | ||||
|  | ||||
|     .loader { | ||||
|       border: 10px solid #f3f3f3; | ||||
|       border-top: 10px solid #8f94ca; | ||||
|       border-radius: 50%; | ||||
|       width: 80px; | ||||
|       height: 80px; | ||||
|       animation: spin .35s linear infinite; | ||||
|     } | ||||
|  | ||||
|     @keyframes spin { | ||||
|       0% { | ||||
|         transform: rotate(0deg); | ||||
|     <!-- Loading styles --> | ||||
|     <style> | ||||
|       .loader-container { | ||||
|         display: flex; | ||||
|         justify-content: center; | ||||
|         align-items: center; | ||||
|         margin: 0; | ||||
|         position: absolute; | ||||
|         top: 50%; | ||||
|         left: 50%; | ||||
|         -ms-transform: translate(-50%, -50%); | ||||
|         transform: translate(-50%, -50%); | ||||
|       } | ||||
|  | ||||
|       100% { | ||||
|         transform: rotate(360deg); | ||||
|       .loader { | ||||
|         border: 10px solid #f3f3f3; | ||||
|         border-top: 10px solid #8f94ca; | ||||
|         border-radius: 50%; | ||||
|         width: 80px; | ||||
|         height: 80px; | ||||
|         animation: spin 0.35s linear infinite; | ||||
|       } | ||||
|     } | ||||
|   </style> | ||||
|  | ||||
|   <title>Solian</title> | ||||
|   <link rel="manifest" href="manifest.json"> | ||||
| </head> | ||||
|       @keyframes spin { | ||||
|         0% { | ||||
|           transform: rotate(0deg); | ||||
|         } | ||||
|  | ||||
| <body> | ||||
|   <div class="loader-container"> | ||||
|     <div class="loader"></div> | ||||
|   </div> | ||||
|         100% { | ||||
|           transform: rotate(360deg); | ||||
|         } | ||||
|       } | ||||
|     </style> | ||||
|  | ||||
|   <script src="flutter_bootstrap.js" async></script> | ||||
| </body> | ||||
|     <title>Solian</title> | ||||
|     <link rel="manifest" href="manifest.json" /> | ||||
|   </head> | ||||
|  | ||||
|   <body> | ||||
|     <div class="loader-container"> | ||||
|       <div class="loader"></div> | ||||
|     </div> | ||||
|  | ||||
|     <script src="flutter_bootstrap.js" async></script> | ||||
|   </body> | ||||
| </html> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user