Skip to content

Commit 36ffdeb

Browse files
committed
feat: theme mode option
1 parent f7e649f commit 36ffdeb

File tree

8 files changed

+95
-15
lines changed

8 files changed

+95
-15
lines changed

assets/i18n/en.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,23 @@
6262
"tmdb-key-subtitle": "Get API Key for TMDB metadata",
6363
"upgrade": "Update Software",
6464
"upgrade-subtitle": "version: {version}",
65-
"upgrade-training": "Check for Updates",
65+
"upgrade-training": "Check",
6666
"auto-check-update": "Automatically Check for Updates",
6767
"auto-check-update-subtitle": "Check for updates on each startup",
6868
"language": "Language",
69+
"theme": "Theme",
70+
"theme-subtitle": "Change the theme of the software",
71+
"theme-system": "System",
72+
"theme-light": "Light",
73+
"theme-dark": "Dark",
6974
"language-subtitle": "Change the language of the software",
7075
"extension-log": "Extension Log Window",
7176
"extension-log-subtitle": "Used for debugging extensions",
7277
"about": "About",
7378
"official-site": "Official Website",
74-
"official-site-training": "Visit the official website",
79+
"official-site-training": "Visit",
7580
"source-code": "Open Source",
76-
"source-code-training": "Go to Star",
81+
"source-code-training": "Star",
7782
"license": "License",
7883
"license-subtitle": "License"
7984
},
@@ -95,6 +100,7 @@
95100
"no-episodes": "No episodes",
96101
"play-complete": "Playback complete",
97102
"resume-last-playback": "Resume last playback",
103+
"subtitle-none": "No Subtitle",
98104
"subtitle": "Subtitle",
99105
"subtitle-change": "Change Subtitle {title}",
100106
"subtitle-file": "Subtitle File"

assets/i18n/zh.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@
7272
"auto-check-update": "自动检查更新",
7373
"auto-check-update-subtitle": "每次启动时检查更新",
7474
"language": "语言",
75+
"theme": "主题",
76+
"theme-subtitle": "选择软件的主题",
77+
"theme-system": "跟随系统",
78+
"theme-light": "浅色",
79+
"theme-dark": "深色",
7580
"language-subtitle": "选择软件的语言",
7681
"extension-log": "扩展日志窗口",
7782
"extension-log-subtitle": "用于调试扩展",

lib/controller.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:get/get.dart';
3+
import 'package:miru_app/utils/miru_storage.dart';
4+
5+
class ApplicationController extends GetxController {
6+
static get find => Get.find();
7+
8+
final themeText = "system".obs;
9+
10+
@override
11+
void onInit() {
12+
themeText.value = MiruStorage.getSetting(SettingKey.theme);
13+
super.onInit();
14+
}
15+
16+
ThemeMode get theme {
17+
switch (themeText.value) {
18+
case "light":
19+
return ThemeMode.light;
20+
case "dark":
21+
return ThemeMode.dark;
22+
default:
23+
return ThemeMode.system;
24+
}
25+
}
26+
27+
changeTheme(String mode) {
28+
MiruStorage.setSetting(SettingKey.theme, mode);
29+
themeText.value = mode;
30+
Get.forceAppUpdate();
31+
}
32+
}

lib/main.dart

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:flutter/services.dart';
88
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
99
import 'package:get/get.dart';
1010
import 'package:media_kit/media_kit.dart';
11+
import 'package:miru_app/controller.dart';
1112
import 'package:miru_app/pages/extension_log/view.dart';
1213
import 'package:miru_app/pages/main/view.dart';
1314
import 'package:miru_app/router/router.dart';
@@ -73,14 +74,27 @@ void main(List<String> args) async {
7374
runApp(const MainApp());
7475
}
7576

76-
class MainApp extends StatelessWidget {
77+
class MainApp extends fluent.StatefulWidget {
7778
const MainApp({super.key});
7879

80+
@override
81+
fluent.State<MainApp> createState() => _MainAppState();
82+
}
83+
84+
class _MainAppState extends fluent.State<MainApp> {
85+
late ApplicationController c;
86+
87+
@override
88+
void initState() {
89+
c = Get.put(ApplicationController());
90+
super.initState();
91+
}
92+
7993
Widget _buildMobileMain(BuildContext context) {
8094
return GetMaterialApp(
8195
title: "Miru",
8296
debugShowCheckedModeBanner: false,
83-
themeMode: ThemeMode.system,
97+
themeMode: c.theme,
8498
theme: ThemeData(useMaterial3: true),
8599
darkTheme: ThemeData.dark(useMaterial3: true),
86100
home: const AndroidMainPage(),
@@ -95,7 +109,7 @@ class MainApp extends StatelessWidget {
95109
title: 'Miru',
96110
debugShowCheckedModeBanner: false,
97111
routerConfig: router,
98-
themeMode: ThemeMode.system,
112+
themeMode: c.theme,
99113
darkTheme: fluent.FluentThemeData(
100114
brightness: Brightness.dark,
101115
visualDensity: VisualDensity.standard,

lib/pages/main/controller.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:fluent_ui/fluent_ui.dart';
2+
import 'package:flutter/scheduler.dart';
23
import 'package:get/get.dart';
34

45
class MainController extends GetxController {
@@ -11,8 +12,9 @@ class MainController extends GetxController {
1112
List<Widget> actions = <Widget>[].obs;
1213

1314
setAcitons(List<Widget> list) async {
14-
await Future.delayed(const Duration(milliseconds: 1));
15-
actions.clear();
16-
actions.addAll(list);
15+
SchedulerBinding.instance.addPostFrameCallback((_) {
16+
actions.clear();
17+
actions.addAll(list);
18+
});
1719
}
1820
}

lib/pages/settings/view.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
55
import 'package:flutter_i18n/flutter_i18n.dart';
66
import 'package:get/get.dart';
77
import 'package:miru_app/api/tmdb.dart';
8+
import 'package:miru_app/controller.dart';
89
import 'package:miru_app/pages/extension_repo/controller.dart';
910
import 'package:miru_app/pages/settings/controller.dart';
1011
import 'package:miru_app/widgets/settings_input_tile.dart';
@@ -163,6 +164,26 @@ class _SettingsPageState extends State<SettingsPage> {
163164
},
164165
),
165166
const SizedBox(height: 8),
167+
SettingsRadiosTile(
168+
icon: const PlatformWidget(
169+
androidWidget: Icon(Icons.color_lens),
170+
desktopWidget: Icon(fluent.FluentIcons.color, size: 24),
171+
),
172+
title: 'settings.theme'.i18n,
173+
itemNameValue: {
174+
'settings.theme-system'.i18n: 'system',
175+
'settings.theme-light'.i18n: 'light',
176+
'settings.theme-dark'.i18n: 'dark',
177+
},
178+
buildSubtitle: () => 'settings.theme-subtitle'.i18n,
179+
applyValue: (value) {
180+
Get.find<ApplicationController>().changeTheme(value);
181+
},
182+
buildGroupValue: () {
183+
return Get.find<ApplicationController>().themeText.value;
184+
},
185+
),
186+
const SizedBox(height: 8),
166187
if (!Platform.isAndroid)
167188
Obx(
168189
() {

lib/utils/miru_storage.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class MiruStorage {
3333
await _initSetting(SettingKey.autoCheckUpdate, true);
3434
await _initSetting(SettingKey.language, 'en');
3535
await _initSetting(SettingKey.novelFontSize, 18.0);
36+
await _initSetting(SettingKey.theme, 'system');
3637
}
3738

3839
static _initSetting(String key, dynamic value) async {
@@ -51,6 +52,7 @@ class MiruStorage {
5152
}
5253

5354
class SettingKey {
55+
static String theme = "Theme";
5456
static String miruRepoUrl = "MiruRepoUrl";
5557
static String tmdbKay = 'TMDBKey';
5658
static String autoCheckUpdate = 'AutoCheckUpdate';

lib/widgets/infinite_scroller.dart

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:io';
22

33
import 'package:flutter/material.dart';
4+
import 'package:flutter/scheduler.dart';
45
import 'package:miru_app/widgets/platform_widget.dart';
56
import 'package:easy_refresh/easy_refresh.dart';
67

@@ -32,16 +33,13 @@ class _InfiniteScrollerState extends State<InfiniteScroller> {
3233
@override
3334
void initState() {
3435
if (!Platform.isAndroid && widget.refreshOnStart) {
35-
_onRefresh();
36+
SchedulerBinding.instance.addPostFrameCallback((_) {
37+
widget.onRefresh();
38+
});
3639
}
3740
super.initState();
3841
}
3942

40-
_onRefresh() async {
41-
await Future.delayed(const Duration(milliseconds: 1));
42-
widget.onRefresh();
43-
}
44-
4543
void _onScroll(ScrollMetrics metrics) {
4644
if (metrics.atEdge && metrics.pixels == metrics.maxScrollExtent) {
4745
if (_isLoding || !widget.enableInfiniteScroll) {

0 commit comments

Comments
 (0)