diff --git a/example/lib/main.dart b/example/lib/main.dart index 565ba60..4dde04e 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -61,7 +61,7 @@ class _MainAppState extends State { debugShowCheckedModeBanner: false, theme: Arcane.theme.light, darkTheme: Arcane.theme.dark, - themeMode: Arcane.theme.currentMode, + themeMode: Arcane.theme.currentTheme, home: Scaffold( appBar: AppBar( title: const Text("Arcane Framework Example"), diff --git a/lib/src/arcane_app.dart b/lib/src/arcane_app.dart index 01ddd0d..fa693a0 100644 --- a/lib/src/arcane_app.dart +++ b/lib/src/arcane_app.dart @@ -46,7 +46,10 @@ class ArcaneApp extends StatelessWidget { Widget build(BuildContext context) { return ArcaneEnvironmentProvider( child: ArcaneServiceProvider( - serviceInstances: services, + serviceInstances: [ + ArcaneReactiveTheme.I, + ...services, + ], child: child, ), ); diff --git a/lib/src/services/reactive_theme/reactive_theme_service.dart b/lib/src/services/reactive_theme/reactive_theme_service.dart index 61f05e3..1ceca61 100644 --- a/lib/src/services/reactive_theme/reactive_theme_service.dart +++ b/lib/src/services/reactive_theme/reactive_theme_service.dart @@ -1,5 +1,4 @@ import "package:arcane_framework/arcane_framework.dart"; -import "package:flutter/foundation.dart"; import "package:flutter/material.dart"; part "reactive_theme_extensions.dart"; @@ -21,10 +20,13 @@ class ArcaneReactiveTheme extends ArcaneService { final ValueNotifier _systemThemeNotifier = ValueNotifier(ThemeMode.light); - /// Returns the current theme mode based on `_isDark`. - /// - /// If `_isDark` is true, it returns `ThemeMode.dark`, otherwise it returns `ThemeMode.light`. - ThemeMode get currentMode => I._systemThemeNotifier.value; + final ValueNotifier _currentThemeNotifier = + ValueNotifier(ThemeMode.light); + + ThemeMode get currentTheme => I._currentThemeNotifier.value; + + /// A listenable that notifies listeners when the system theme mode changes. + ThemeMode get systemTheme => I._systemThemeNotifier.value; /// The `ThemeData` for the dark theme. final ValueNotifier _darkTheme = ValueNotifier(ThemeData.dark()); @@ -40,9 +42,6 @@ class ArcaneReactiveTheme extends ArcaneService { ThemeData get light => _lightTheme.value; ValueNotifier get lightTheme => I._lightTheme; - /// A listenable that notifies listeners when the syste theme mode changes. - ValueListenable get systemTheme => I._systemThemeNotifier; - /// Switches the current theme between light and dark modes. /// /// If the theme is currently light, it switches to dark, and vice versa. It also @@ -53,12 +52,17 @@ class ArcaneReactiveTheme extends ArcaneService { /// ArcaneReactiveTheme.I.switchTheme(); /// ``` ArcaneReactiveTheme switchTheme({ThemeMode? themeMode}) { + if (I._systemThemeNotifier.hasListeners) { + _systemThemeNotifier.removeListener(_systemThemeListener); + } + if (themeMode != null) { - _systemThemeNotifier.value = themeMode; + _currentThemeNotifier.value = themeMode; } else { - _systemThemeNotifier.value = _systemThemeNotifier.value == ThemeMode.light - ? ThemeMode.dark - : ThemeMode.light; + _currentThemeNotifier.value = + _currentThemeNotifier.value == ThemeMode.light + ? ThemeMode.dark + : ThemeMode.light; } notifyListeners(); @@ -78,8 +82,13 @@ class ArcaneReactiveTheme extends ArcaneService { final ThemeMode systemMode = context.isDarkMode ? ThemeMode.dark : ThemeMode.light; - if (currentMode != systemMode) { - switchTheme(); + if (!I._systemThemeNotifier.hasListeners) { + I._systemThemeNotifier.addListener(_systemThemeListener); + } + + if (systemMode != currentTheme) { + _systemThemeNotifier.value = systemMode; + notifyListeners(); } return I; @@ -120,6 +129,14 @@ class ArcaneReactiveTheme extends ArcaneService { _darkTheme.value = ThemeData.dark(); _lightTheme.value = ThemeData.light(); _systemThemeNotifier.value = ThemeMode.light; + _currentThemeNotifier.value = ThemeMode.light; notifyListeners(); } + + void _systemThemeListener() { + if (currentTheme != _systemThemeNotifier.value) { + _currentThemeNotifier.value = _systemThemeNotifier.value; + notifyListeners(); + } + } } diff --git a/test/services/reactive_theme/reactive_theme_service_test.dart b/test/services/reactive_theme/reactive_theme_service_test.dart index e752d9f..53f9b47 100644 --- a/test/services/reactive_theme/reactive_theme_service_test.dart +++ b/test/services/reactive_theme/reactive_theme_service_test.dart @@ -16,15 +16,15 @@ void main() { group("theme mode", () { test("initial mode is light", () { - expect(theme.currentMode, equals(ThemeMode.light)); + expect(theme.currentTheme, equals(ThemeMode.light)); }); test("switchTheme toggles between light and dark", () { - expect(theme.currentMode, equals(ThemeMode.light)); + expect(theme.currentTheme, equals(ThemeMode.light)); theme.switchTheme(); - expect(theme.currentMode, equals(ThemeMode.dark)); + expect(theme.currentTheme, equals(ThemeMode.dark)); theme.switchTheme(); - expect(theme.currentMode, equals(ThemeMode.light)); + expect(theme.currentTheme, equals(ThemeMode.light)); }); test("switching theme notifies listeners", () { @@ -66,7 +66,7 @@ void main() { }); theme.addListener(() { - currentTheme = theme.currentMode; + currentTheme = theme.currentTheme; }); expect(currentTheme, ThemeMode.system); @@ -103,7 +103,7 @@ void main() { final BuildContext lightContext = tester.element(find.byType(SizedBox)); theme.followSystemTheme(lightContext); - expect(theme.currentMode, equals(ThemeMode.light)); + expect(theme.currentTheme, equals(ThemeMode.light)); await tester.pumpWidget( const MediaQuery( @@ -113,9 +113,8 @@ void main() { ); final BuildContext darkContext = tester.element(find.byType(SizedBox)); - theme.followSystemTheme(darkContext); - expect(theme.currentMode, equals(ThemeMode.dark)); + expect(theme.currentTheme, equals(ThemeMode.dark)); }); testWidgets("followSystemTheme only switches when needed",