Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: follow the system theme #1279

Merged
merged 9 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion assets/i18n/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,11 @@
"exportSectionTitle": "Import & export",
"logsSectionTitle": "Logs",

"themeModeLabel": "App Theme",
BenjaminHalko marked this conversation as resolved.
Show resolved Hide resolved
"themeModeHint": "Select the theme to use",
BenjaminHalko marked this conversation as resolved.
Show resolved Hide resolved
"systemThemeLabel": "System default",
"lightThemeLabel": "Light mode",
"darkThemeLabel": "Dark mode",
"darkThemeHint": "Welcome to the dark side",

"dynamicThemeLabel": "Material You",
"dynamicThemeHint": "Enjoy an experience closer to your device",
Expand Down
8 changes: 4 additions & 4 deletions lib/services/manager_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,12 @@ class ManagerAPI {
await _prefs.setBool('useDynamicTheme', value);
}

bool getUseDarkTheme() {
return _prefs.getBool('useDarkTheme') ?? false;
int getThemeMode() {
return _prefs.getInt('themeMode') ?? 2;
}

Future<void> setUseDarkTheme(bool value) async {
await _prefs.setBool('useDarkTheme', value);
Future<void> setThemeMode(int value) async {
await _prefs.setInt('themeMode', value);
}

bool areUniversalPatchesEnabled() {
Expand Down
69 changes: 55 additions & 14 deletions lib/ui/theme/dynamic_theme_builder.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import 'dart:ui';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:dynamic_themes/dynamic_themes.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/app/app.router.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/theme.dart';
import 'package:stacked_services/stacked_services.dart';

class DynamicThemeBuilder extends StatelessWidget {
class DynamicThemeBuilder extends StatefulWidget {
const DynamicThemeBuilder({
Key? key,
required this.title,
Expand All @@ -17,6 +21,35 @@ class DynamicThemeBuilder extends StatelessWidget {
final Widget home;
final Iterable<LocalizationsDelegate> localizationsDelegates;

@override
State<DynamicThemeBuilder> createState() => _DynamicThemeBuilderState();
}

class _DynamicThemeBuilderState extends State<DynamicThemeBuilder> with WidgetsBindingObserver {
Brightness brightness = PlatformDispatcher.instance.platformBrightness;
final ManagerAPI _managerAPI = locator<ManagerAPI>();

@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}

@override
void didChangePlatformBrightness() {
setState(() {
brightness = PlatformDispatcher.instance.platformBrightness;
});
if (_managerAPI.getThemeMode() < 2) {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
systemNavigationBarIconBrightness:
brightness == Brightness.light ? Brightness.dark : Brightness.light,
),
);
}
}

@override
Widget build(BuildContext context) {
return DynamicColorBuilder(
Expand Down Expand Up @@ -50,24 +83,32 @@ class DynamicThemeBuilder extends StatelessWidget {
return DynamicTheme(
themeCollection: ThemeCollection(
themes: {
0: lightCustomTheme,
1: darkCustomTheme,
2: lightDynamicTheme,
3: darkDynamicTheme,
0: brightness == Brightness.light ? lightCustomTheme : darkCustomTheme,
1: brightness == Brightness.light ? lightDynamicTheme : darkDynamicTheme,
2: lightCustomTheme,
3: lightDynamicTheme,
4: darkCustomTheme,
5: darkDynamicTheme,
},
fallbackTheme: lightCustomTheme,
fallbackTheme: brightness == Brightness.light ? lightCustomTheme : darkCustomTheme,
),
builder: (context, theme) => MaterialApp(
debugShowCheckedModeBanner: false,
title: title,
navigatorKey: StackedService.navigatorKey,
onGenerateRoute: StackedRouter().onGenerateRoute,
theme: theme,
home: home,
localizationsDelegates: localizationsDelegates,
),
debugShowCheckedModeBanner: false,
title: widget.title,
navigatorKey: StackedService.navigatorKey,
onGenerateRoute: StackedRouter().onGenerateRoute,
theme: theme,
home: widget.home,
localizationsDelegates: widget.localizationsDelegates,
),
);
},
);
}

@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
}
7 changes: 2 additions & 5 deletions lib/ui/views/navigation/navigation_viewmodel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,8 @@ class NavigationViewModel extends IndexTrackingViewModel {
);
}

if (prefs.getBool('useDarkTheme') == null) {
final bool isDark =
MediaQuery.platformBrightnessOf(context) != Brightness.light;
await prefs.setBool('useDarkTheme', isDark);
await DynamicTheme.of(context)!.setTheme(isDark ? 1 : 0);
if (prefs.getInt('themeMode') == null) {
await prefs.setInt('themeMode', 0);
}
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
SystemChrome.setSystemUIOverlayStyle(
Expand Down
104 changes: 80 additions & 24 deletions lib/ui/views/settings/settingsFragment/settings_update_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
import 'package:stacked/stacked.dart';

final _settingViewModel = SettingsViewModel();
Expand All @@ -24,37 +25,93 @@ class SUpdateTheme extends BaseViewModel {

Future<void> setUseDynamicTheme(BuildContext context, bool value) async {
await _managerAPI.setUseDynamicTheme(value);
final int currentTheme = DynamicTheme.of(context)!.themeId;
if (currentTheme.isEven) {
await DynamicTheme.of(context)!.setTheme(value ? 2 : 0);
} else {
await DynamicTheme.of(context)!.setTheme(value ? 3 : 1);
}
final int currentTheme = (DynamicTheme.of(context)!.themeId ~/ 2) * 2;
await DynamicTheme.of(context)!.setTheme(currentTheme + (value ? 1 : 0));
notifyListeners();
}

bool getDarkThemeStatus() {
return _managerAPI.getUseDarkTheme();
int getThemeMode() {
return _managerAPI.getThemeMode();
}

Future<void> setUseDarkTheme(BuildContext context, bool value) async {
await _managerAPI.setUseDarkTheme(value);
final int currentTheme = DynamicTheme.of(context)!.themeId;
if (currentTheme < 2) {
await DynamicTheme.of(context)!.setTheme(value ? 1 : 0);
} else {
await DynamicTheme.of(context)!.setTheme(value ? 3 : 2);
}
Future<void> setThemeMode(BuildContext context, int value) async {
await _managerAPI.setThemeMode(value);
final bool isDynamicTheme = DynamicTheme.of(context)!.themeId.isEven;
await DynamicTheme.of(context)!.setTheme(value * 2 + (isDynamicTheme ? 0 : 1));
final bool isLight = value != 2 && (value == 1 || DynamicTheme.of(context)!.theme.brightness == Brightness.light);
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
systemNavigationBarIconBrightness:
value ? Brightness.light : Brightness.dark,
isLight ? Brightness.dark : Brightness.light,
),
);
notifyListeners();
}

I18nText getThemeModeName() {
final int = getThemeMode();
switch (int) {
case 0:
return I18nText('settingsView.systemThemeLabel');
case 1:
return I18nText('settingsView.lightThemeLabel');
case 2:
return I18nText('settingsView.darkThemeLabel');
default:
return I18nText('settingsView.systemThemeLabel');
}
}

Future<void> showThemeDialog(BuildContext context) async {
int? currentTheme = getThemeMode();

return showDialog(
context: context,
builder: (context) => AlertDialog(
title: I18nText('settingsView.themeModeLabel'),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
actions: <Widget>[
Column(
children: <Widget>[
RadioListTile(
title: I18nText('settingsView.systemThemeLabel'),
value: 0,
groupValue: currentTheme,
onChanged: (value) => {
currentTheme = value,
setThemeMode(context, value!),
Navigator.of(context).pop(),
},
),
RadioListTile(
title: I18nText('settingsView.lightThemeLabel'),
value: 1,
groupValue: currentTheme,
onChanged: (value) => {
currentTheme = value,
setThemeMode(context, value!),
Navigator.of(context).pop(),
},
),
RadioListTile(
title: I18nText('settingsView.darkThemeLabel'),
value: 2,
groupValue: currentTheme,
onChanged: (value) => {
currentTheme = value,
setThemeMode(context, value!),
Navigator.of(context).pop(),
},
),
],
),
],
),
);
}
}

final sUpdateTheme = SUpdateTheme();
class SUpdateThemeUI extends StatelessWidget {
const SUpdateThemeUI({super.key});

Expand All @@ -63,10 +120,10 @@ class SUpdateThemeUI extends StatelessWidget {
return SettingsSection(
title: 'settingsView.appearanceSectionTitle',
children: <Widget>[
SwitchListTile(
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.darkThemeLabel',
'settingsView.themeModeLabel',
child: const Text(
'',
style: TextStyle(
Expand All @@ -75,11 +132,10 @@ class SUpdateThemeUI extends StatelessWidget {
),
),
),
subtitle: I18nText('settingsView.darkThemeHint'),
value: SUpdateTheme().getDarkThemeStatus(),
onChanged: (value) => SUpdateTheme().setUseDarkTheme(
context,
value,
subtitle: I18nText('settingsView.themeModeHint'),
trailing: CustomMaterialButton(
label: sUpdateTheme.getThemeModeName(),
onPressed: () => { sUpdateTheme.showThemeDialog(context) },
),
),
FutureBuilder<int>(
Expand Down