Fixes the reactive theme service to properly follow the system brightness

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2025-04-23 20:56:49 +02:00
parent 58817b349d
commit cfd9052442
4 changed files with 150 additions and 72 deletions
@@ -15,7 +15,7 @@ extension DarkMode on BuildContext {
/// }
/// ```
bool get isDarkMode {
final brightness = MediaQuery.of(this).platformBrightness;
final brightness = MediaQuery.platformBrightnessOf(this);
return brightness == Brightness.dark;
}
}
@@ -1,3 +1,5 @@
import "dart:async";
import "package:arcane_framework/arcane_framework.dart";
import "package:flutter/material.dart";
@@ -8,7 +10,7 @@ part "reactive_theme_extensions.dart";
/// `ArcaneReactiveTheme` allows switching between light and dark themes and provides
/// methods to customize the themes. The current theme mode can be accessed, and the
/// theme can be switched at runtime.
class ArcaneReactiveTheme extends ArcaneService {
class ArcaneReactiveTheme extends ArcaneService with WidgetsBindingObserver {
/// The singleton instance of `ArcaneReactiveTheme`.
static final ArcaneReactiveTheme _instance = ArcaneReactiveTheme._internal();
@@ -17,13 +19,24 @@ class ArcaneReactiveTheme extends ArcaneService {
ArcaneReactiveTheme._internal();
// Whether to follow system theme
bool _followingSystemTheme = false;
final ValueNotifier<ThemeMode> _systemThemeNotifier =
ValueNotifier(ThemeMode.light);
ValueNotifier(ThemeMode.system);
final ValueNotifier<ThemeMode> _currentThemeNotifier =
ValueNotifier(ThemeMode.light);
final StreamController<ThemeMode> _themeStreamController =
StreamController<ThemeMode>.broadcast(
onCancel: () {
I._themeStreamController.close();
},
);
ThemeMode get currentTheme => I._currentThemeNotifier.value;
Stream<ThemeMode> get currentThemeStream => I._themeStreamController.stream;
ThemeMode _currentTheme = ThemeMode.light;
ThemeMode get currentTheme => _currentTheme;
/// A listenable that notifies listeners when the system theme mode changes.
ThemeMode get systemTheme => I._systemThemeNotifier.value;
@@ -52,21 +65,17 @@ class ArcaneReactiveTheme extends ArcaneService {
/// ArcaneReactiveTheme.I.switchTheme();
/// ```
ArcaneReactiveTheme switchTheme({ThemeMode? themeMode}) {
if (I._systemThemeNotifier.hasListeners) {
_systemThemeNotifier.removeListener(_systemThemeListener);
}
_followingSystemTheme = false;
if (themeMode != null) {
_currentThemeNotifier.value = themeMode;
_updateTheme(themeMode);
} else {
_currentThemeNotifier.value =
_currentThemeNotifier.value == ThemeMode.light
? ThemeMode.dark
: ThemeMode.light;
_updateTheme(
currentTheme == ThemeMode.dark ? ThemeMode.light : ThemeMode.dark,
);
}
notifyListeners();
return I;
}
@@ -79,21 +88,33 @@ class ArcaneReactiveTheme extends ArcaneService {
/// final ThemeMode mode = Arcane.theme.systemTheme.value;
/// ```
ArcaneReactiveTheme followSystemTheme(BuildContext context) {
final ThemeMode systemMode =
context.isDarkMode ? ThemeMode.dark : ThemeMode.light;
if (!_followingSystemTheme) {
_followingSystemTheme = true;
if (!I._systemThemeNotifier.hasListeners) {
I._systemThemeNotifier.addListener(_systemThemeListener);
}
if (systemMode != currentTheme) {
_systemThemeNotifier.value = systemMode;
notifyListeners();
WidgetsBinding.instance.addPostFrameCallback((_) {
checkSystemTheme(context);
});
}
return I;
}
/// Check and apply the system theme if we're following it
void checkSystemTheme(BuildContext context) {
if (!_followingSystemTheme) return;
final Brightness systemBrightness =
MediaQuery.platformBrightnessOf(context);
final ThemeMode systemMode =
systemBrightness == Brightness.dark ? ThemeMode.dark : ThemeMode.light;
if (systemMode != _currentTheme) {
_updateTheme(systemMode);
notifyListeners();
}
}
/// Sets a custom `ThemeData` for the dark theme.
///
/// This allows you to customize the dark theme and notify listeners to apply the
@@ -128,15 +149,13 @@ class ArcaneReactiveTheme extends ArcaneService {
void reset() {
_darkTheme.value = ThemeData.dark();
_lightTheme.value = ThemeData.light();
_systemThemeNotifier.value = ThemeMode.light;
_currentThemeNotifier.value = ThemeMode.light;
_followingSystemTheme = false;
_updateTheme(ThemeMode.light);
notifyListeners();
}
void _systemThemeListener() {
if (currentTheme != _systemThemeNotifier.value) {
_currentThemeNotifier.value = _systemThemeNotifier.value;
notifyListeners();
}
void _updateTheme(ThemeMode themeMode) {
_currentTheme = themeMode;
_themeStreamController.add(themeMode);
}
}