Make theme switcher easier to use

This commit is contained in:
LittleSheep 2024-07-31 22:48:22 +08:00
parent 31d50bfb1f
commit 52e58fce3d
9 changed files with 149 additions and 111 deletions

View File

@ -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(),
),
),
);
},
);
},
}),
);
}

View 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();
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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.',
};

View File

@ -289,5 +289,5 @@ const i18nSimplifiedChinese = {
'themeColorMiku': '未来色',
'themeColorKagamine': '镜音黄',
'themeColorLuka': '流音粉',
'themeColorApplied': '全局主题颜色已应用,深色模式中主题需要重启生效',
'themeColorApplied': '全局主题颜色已应用',
};

View File

@ -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:

View File

@ -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:

View File

@ -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>