♿ Make theme switcher easier to use
This commit is contained in:
parent
31d50bfb1f
commit
52e58fce3d
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user