Skip to content

Commit ff83c2c

Browse files
authored
feat: multiple theming options (#50)
* refactor: consolidate theme definitions by removing separate dark and light theme files * feat: integrate dynamic color support and enhance theme settings management * feat: add theme settings route and update theme management in app settings * feat: enhance theme management by integrating high contrast support in various components * feat: implement mode selection dialog for theme settings and enhance button functionality * refactor: update theme import paths and consolidate theme provider files * feat: enhance theme management by integrating theme selection based on audiobook playback * refactor: update default value for useMaterialThemeFromSystem to false in theme settings * refactor: adjust high contrast condition order in theme settings for consistency * refactor: rename useMaterialThemeOfPlayingItem to useCurrentPlayerThemeThroughoutApp for clarity * refactor: correct spelling in system theme provider and replace with updated implementation * refactor: extract restore backup dialog into a separate widget for improved readability * refactor: reorganize settings sections for clarity and improve restore dialog functionality
1 parent 758e4cd commit ff83c2c

28 files changed

+933
-192
lines changed

.vscode/settings.json

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"audioplayers",
1313
"autolabeler",
1414
"Autovalidate",
15+
"Checkmark",
1516
"deeplinking",
1617
"fullscreen",
1718
"Lerp",

lib/features/item_viewer/view/library_item_hero_section.dart

+6-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import 'package:vaani/settings/app_settings_provider.dart';
1515
import 'package:vaani/shared/extensions/duration_format.dart';
1616
import 'package:vaani/shared/extensions/model_conversions.dart';
1717
import 'package:vaani/shared/widgets/shelves/book_shelf.dart';
18-
import 'package:vaani/theme/theme_from_cover_provider.dart';
18+
import 'package:vaani/theme/providers/theme_from_cover_provider.dart';
1919

2020
class LibraryItemHeroSection extends HookConsumerWidget {
2121
const LibraryItemHeroSection({
@@ -353,16 +353,17 @@ class _BookCover extends HookConsumerWidget {
353353
final coverImage = ref.watch(coverImageProvider(itemId));
354354
final themeData = Theme.of(context);
355355
// final item = ref.watch(libraryItemProvider(itemId));
356-
final useMaterialThemeOnItemPage =
357-
ref.watch(appSettingsProvider).themeSettings.useMaterialThemeOnItemPage;
356+
final themeSettings = ref.watch(appSettingsProvider).themeSettings;
358357

359358
ColorScheme? coverColorScheme;
360-
if (useMaterialThemeOnItemPage) {
359+
if (themeSettings.useMaterialThemeOnItemPage) {
361360
coverColorScheme = ref
362361
.watch(
363362
themeOfLibraryItemProvider(
364363
itemId,
365364
brightness: Theme.of(context).brightness,
365+
highContrast: themeSettings.highContrast ||
366+
MediaQuery.of(context).highContrast,
366367
),
367368
)
368369
.valueOrNull;
@@ -371,7 +372,7 @@ class _BookCover extends HookConsumerWidget {
371372
return ThemeSwitcher(
372373
builder: (context) {
373374
// change theme after 2 seconds
374-
if (useMaterialThemeOnItemPage) {
375+
if (themeSettings.useMaterialThemeOnItemPage) {
375376
Future.delayed(150.ms, () {
376377
try {
377378
ThemeSwitcher.of(context).changeTheme(

lib/features/player/view/audiobook_player.dart

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import 'package:vaani/features/player/providers/player_form.dart';
1414
import 'package:vaani/settings/app_settings_provider.dart';
1515
import 'package:vaani/shared/extensions/inverse_lerp.dart';
1616
import 'package:vaani/shared/widgets/shelves/book_shelf.dart';
17-
import 'package:vaani/theme/theme_from_cover_provider.dart';
17+
import 'package:vaani/theme/providers/theme_from_cover_provider.dart';
1818

1919
import 'player_when_expanded.dart';
2020
import 'player_when_minimized.dart';
@@ -65,6 +65,8 @@ class AudiobookPlayer extends HookConsumerWidget {
6565
themeOfLibraryItemProvider(
6666
itemBeingPlayed.valueOrNull?.id,
6767
brightness: Theme.of(context).brightness,
68+
highContrast: appSettings.themeSettings.highContrast ||
69+
MediaQuery.of(context).highContrast,
6870
),
6971
);
7072

lib/main.dart

+71-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:dynamic_color/dynamic_color.dart';
12
import 'package:flutter/material.dart';
23
import 'package:hooks_riverpod/hooks_riverpod.dart';
34
import 'package:logging/logging.dart';
@@ -7,12 +8,15 @@ import 'package:vaani/features/downloads/providers/download_manager.dart';
78
import 'package:vaani/features/logging/core/logger.dart';
89
import 'package:vaani/features/playback_reporting/providers/playback_reporter_provider.dart';
910
import 'package:vaani/features/player/core/init.dart';
10-
import 'package:vaani/features/player/providers/audiobook_player.dart';
11+
import 'package:vaani/features/player/providers/audiobook_player.dart'
12+
show audiobookPlayerProvider, simpleAudiobookPlayerProvider;
1113
import 'package:vaani/features/shake_detection/providers/shake_detector.dart';
1214
import 'package:vaani/features/sleep_timer/providers/sleep_timer_provider.dart';
1315
import 'package:vaani/router/router.dart';
1416
import 'package:vaani/settings/api_settings_provider.dart';
1517
import 'package:vaani/settings/app_settings_provider.dart';
18+
import 'package:vaani/theme/providers/system_theme_provider.dart';
19+
import 'package:vaani/theme/providers/theme_from_cover_provider.dart';
1620
import 'package:vaani/theme/theme.dart';
1721

1822
final appLogger = Logger('vaani');
@@ -51,16 +55,77 @@ class MyApp extends ConsumerWidget {
5155
if (needOnboarding) {
5256
routerConfig.goNamed(Routes.onboarding.name);
5357
}
58+
final appSettings = ref.watch(appSettingsProvider);
59+
final themeSettings = appSettings.themeSettings;
5460

61+
ColorScheme lightColorScheme = brandLightColorScheme;
62+
ColorScheme darkColorScheme = brandDarkColorScheme;
63+
64+
final shouldUseHighContrast =
65+
themeSettings.highContrast || MediaQuery.of(context).highContrast;
66+
67+
if (shouldUseHighContrast) {
68+
lightColorScheme = lightColorScheme.copyWith(
69+
surface: Colors.white,
70+
);
71+
darkColorScheme = darkColorScheme.copyWith(
72+
surface: Colors.black,
73+
);
74+
}
75+
76+
if (themeSettings.useMaterialThemeFromSystem) {
77+
var themes =
78+
ref.watch(systemThemeProvider(highContrast: shouldUseHighContrast));
79+
if (themes.valueOrNull != null) {
80+
lightColorScheme = themes.valueOrNull!.$1;
81+
darkColorScheme = themes.valueOrNull!.$2;
82+
}
83+
}
84+
85+
if (themeSettings.useCurrentPlayerThemeThroughoutApp) {
86+
final player = ref.watch(audiobookPlayerProvider);
87+
if (player.book != null) {
88+
final themeLight = ref.watch(
89+
themeOfLibraryItemProvider(
90+
player.book!.libraryItemId,
91+
highContrast: shouldUseHighContrast,
92+
brightness: Brightness.light,
93+
),
94+
);
95+
final themeDark = ref.watch(
96+
themeOfLibraryItemProvider(
97+
player.book!.libraryItemId,
98+
highContrast: shouldUseHighContrast,
99+
brightness: Brightness.dark,
100+
),
101+
);
102+
if (themeLight.valueOrNull != null && themeDark.valueOrNull != null) {
103+
lightColorScheme = themeLight.valueOrNull!;
104+
darkColorScheme = themeDark.valueOrNull!;
105+
}
106+
}
107+
}
108+
final appThemeLight = ThemeData(
109+
useMaterial3: true,
110+
colorScheme: lightColorScheme.harmonized(),
111+
);
112+
final appThemeDark = ThemeData(
113+
useMaterial3: true,
114+
colorScheme: darkColorScheme.harmonized(),
115+
brightness: Brightness.dark,
116+
// TODO bottom sheet theme is not working
117+
bottomSheetTheme: BottomSheetThemeData(
118+
backgroundColor: darkColorScheme.surface,
119+
),
120+
);
55121
try {
56122
return MaterialApp.router(
57123
// debugShowCheckedModeBanner: false,
58-
theme: lightTheme,
59-
darkTheme: darkTheme,
60-
themeMode: ref.watch(appSettingsProvider).themeSettings.isDarkMode
61-
? ThemeMode.dark
62-
: ThemeMode.light,
124+
theme: appThemeLight,
125+
darkTheme: appThemeDark,
126+
themeMode: themeSettings.themeMode,
63127
routerConfig: routerConfig,
128+
themeAnimationCurve: Curves.easeInOut,
64129
);
65130
} catch (e) {
66131
debugPrintStack(stackTrace: StackTrace.current, label: e.toString());

lib/router/constants.dart

+5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ class Routes {
2727
pathName: 'config',
2828
name: 'settings',
2929
);
30+
static const themeSettings = _SimpleRoute(
31+
pathName: 'theme',
32+
name: 'themeSettings',
33+
parentRoute: settings,
34+
);
3035
static const autoSleepTimerSettings = _SimpleRoute(
3136
pathName: 'autoSleepTimer',
3237
name: 'autoSleepTimerSettings',

lib/router/router.dart

+8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import 'package:vaani/settings/view/auto_sleep_timer_settings_page.dart';
1717
import 'package:vaani/settings/view/notification_settings_page.dart';
1818
import 'package:vaani/settings/view/player_settings_page.dart';
1919
import 'package:vaani/settings/view/shake_detector_settings_page.dart';
20+
import 'package:vaani/settings/view/theme_settings_page.dart';
2021

2122
import 'scaffold_with_nav_bar.dart';
2223
import 'transitions/slide.dart';
@@ -178,6 +179,13 @@ class MyAppRouter {
178179
// builder: (context, state) => const AppSettingsPage(),
179180
pageBuilder: defaultPageBuilder(const AppSettingsPage()),
180181
routes: [
182+
GoRoute(
183+
path: Routes.themeSettings.pathName,
184+
name: Routes.themeSettings.name,
185+
pageBuilder: defaultPageBuilder(
186+
const ThemeSettingsPage(),
187+
),
188+
),
181189
GoRoute(
182190
path: Routes.autoSleepTimerSettings.pathName,
183191
name: Routes.autoSleepTimerSettings.name,

lib/settings/app_settings_provider.dart

-5
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,6 @@ class AppSettings extends _$AppSettings {
4747
_logger.fine('wrote settings to box: $state');
4848
}
4949

50-
void toggleDarkMode() {
51-
state = state.copyWith
52-
.themeSettings(isDarkMode: !state.themeSettings.isDarkMode);
53-
}
54-
5550
void update(model.AppSettings newSettings) {
5651
state = newSettings;
5752
}

lib/settings/app_settings_provider.g.dart

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/settings/models/app_settings.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ class AppSettings with _$AppSettings {
2828
@freezed
2929
class ThemeSettings with _$ThemeSettings {
3030
const factory ThemeSettings({
31-
@Default(true) bool isDarkMode,
31+
@Default(ThemeMode.system) ThemeMode themeMode,
32+
@Default(false) bool highContrast,
33+
@Default(false) bool useMaterialThemeFromSystem,
34+
@Default('#FF311B92') String customThemeColor,
3235
@Default(true) bool useMaterialThemeOnItemPage,
3336
@Default(true) bool useCurrentPlayerThemeThroughoutApp,
3437
}) = _ThemeSettings;

0 commit comments

Comments
 (0)