mirror of
https://github.com/hanskokx/arcane_framework.git
synced 2026-05-14 10:29:06 +02:00
Fixes tests and updates reactive theme
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -59,7 +59,8 @@ class MainApp extends StatelessWidget {
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: Arcane.theme.light,
|
||||
darkTheme: Arcane.theme.dark,
|
||||
themeMode: Arcane.theme.currentTheme,
|
||||
themeMode:
|
||||
ArcaneTheme.of(context)?.themeMode ?? Arcane.theme.currentTheme,
|
||||
home: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("Arcane Framework Example"),
|
||||
|
||||
@@ -45,4 +45,5 @@ export "package:arcane_framework/src/services/authentication/authentication_serv
|
||||
export "package:arcane_framework/src/services/feature_flags/feature_flags_service.dart";
|
||||
export "package:arcane_framework/src/services/logging/logging_service.dart";
|
||||
export "package:arcane_framework/src/services/reactive_theme/reactive_theme_service.dart";
|
||||
export "package:arcane_framework/src/services/reactive_theme/reactive_theme_wrapper.dart";
|
||||
export "package:result_monad/result_monad.dart";
|
||||
|
||||
+16
-20
@@ -56,16 +56,15 @@ class _ArcaneAppState extends State<ArcaneApp> with WidgetsBindingObserver {
|
||||
serviceInstances: widget.services,
|
||||
child: Builder(
|
||||
key: _appKey,
|
||||
builder: (context) {
|
||||
_updateContextReference(context);
|
||||
|
||||
builder: (BuildContext currentContext) {
|
||||
return StreamBuilder<ThemeMode>(
|
||||
stream: ArcaneReactiveTheme.I.currentThemeStream,
|
||||
initialData: ArcaneReactiveTheme.I.currentTheme,
|
||||
builder: (context, AsyncSnapshot<ThemeMode> snapshot) {
|
||||
if (!snapshot.hasData) return widget.child;
|
||||
final ThemeMode themeMode = snapshot.data ?? ThemeMode.light;
|
||||
|
||||
return KeyedSubtree(
|
||||
key: Key(snapshot.data!.name),
|
||||
return ArcaneTheme(
|
||||
themeMode: themeMode,
|
||||
child: widget.child,
|
||||
);
|
||||
},
|
||||
@@ -76,17 +75,6 @@ class _ArcaneAppState extends State<ArcaneApp> with WidgetsBindingObserver {
|
||||
);
|
||||
}
|
||||
|
||||
// Update our context reference whenever the widget is built
|
||||
void _updateContextReference(BuildContext context) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// Only store this context if the widget is still mounted
|
||||
if (mounted) {
|
||||
// Store this context in a way that ArcaneReactiveTheme can access it
|
||||
ArcaneReactiveTheme.I.checkSystemTheme(context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -103,9 +91,17 @@ class _ArcaneAppState extends State<ArcaneApp> with WidgetsBindingObserver {
|
||||
|
||||
@override
|
||||
void didChangePlatformBrightness() {
|
||||
// This is called when the system brightness changes
|
||||
// Check and update the theme if we're following system theme
|
||||
ArcaneReactiveTheme.I.checkSystemTheme(context);
|
||||
// When system brightness changes, find the current builder context
|
||||
// and use it to check the system theme
|
||||
if (mounted && _appKey.currentContext != null) {
|
||||
// Use the current context from the key to check system theme
|
||||
final BuildContext currentContext = _appKey.currentContext!;
|
||||
if (ArcaneReactiveTheme.I.isFollowingSystemTheme) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
ArcaneReactiveTheme.I.checkSystemTheme(currentContext);
|
||||
});
|
||||
}
|
||||
}
|
||||
super.didChangePlatformBrightness();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,10 @@ 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 with WidgetsBindingObserver {
|
||||
///
|
||||
/// System theme changes are detected by the `ArcaneApp` widget, which ensures
|
||||
/// theme updates happen automatically when the device theme changes.
|
||||
class ArcaneReactiveTheme extends ArcaneService {
|
||||
/// The singleton instance of `ArcaneReactiveTheme`.
|
||||
static final ArcaneReactiveTheme _instance = ArcaneReactiveTheme._internal();
|
||||
|
||||
@@ -22,6 +25,12 @@ class ArcaneReactiveTheme extends ArcaneService with WidgetsBindingObserver {
|
||||
// Whether to follow system theme
|
||||
bool _followingSystemTheme = false;
|
||||
|
||||
/// Whether the theme service is currently following the system theme.
|
||||
///
|
||||
/// When true, the theme will automatically switch between light and dark
|
||||
/// based on the system's brightness setting.
|
||||
bool get isFollowingSystemTheme => _followingSystemTheme;
|
||||
|
||||
final ValueNotifier<ThemeMode> _systemThemeNotifier =
|
||||
ValueNotifier(ThemeMode.system);
|
||||
|
||||
@@ -32,10 +41,12 @@ class ArcaneReactiveTheme extends ArcaneService with WidgetsBindingObserver {
|
||||
},
|
||||
);
|
||||
|
||||
/// Stream of theme mode changes that can be listened to for reactive UI updates.
|
||||
Stream<ThemeMode> get currentThemeStream => I._themeStreamController.stream;
|
||||
|
||||
ThemeMode _currentTheme = ThemeMode.light;
|
||||
|
||||
/// The currently active theme mode (light or dark).
|
||||
ThemeMode get currentTheme => _currentTheme;
|
||||
|
||||
/// A listenable that notifies listeners when the system theme mode changes.
|
||||
@@ -49,10 +60,14 @@ class ArcaneReactiveTheme extends ArcaneService with WidgetsBindingObserver {
|
||||
|
||||
/// Returns the current dark theme `ThemeData`.
|
||||
ThemeData get dark => _darkTheme.value;
|
||||
|
||||
/// ValueNotifier for the dark theme that can be observed for changes.
|
||||
ValueNotifier<ThemeData> get darkTheme => I._darkTheme;
|
||||
|
||||
/// Returns the current light theme `ThemeData`.
|
||||
ThemeData get light => _lightTheme.value;
|
||||
|
||||
/// ValueNotifier for the light theme that can be observed for changes.
|
||||
ValueNotifier<ThemeData> get lightTheme => I._lightTheme;
|
||||
|
||||
/// Switches the current theme between light and dark modes.
|
||||
@@ -82,24 +97,26 @@ class ArcaneReactiveTheme extends ArcaneService with WidgetsBindingObserver {
|
||||
/// Switches the current theme between light and dark modes automatically
|
||||
/// based upon the system's current mode.
|
||||
///
|
||||
/// This will also register for system theme changes, so the theme will
|
||||
/// automatically update when the system theme changes.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// ArcaneReactiveTheme.I.followSystemTheme(context);
|
||||
/// final ThemeMode mode = Arcane.theme.systemTheme.value;
|
||||
/// ```
|
||||
ArcaneReactiveTheme followSystemTheme(BuildContext context) {
|
||||
if (!_followingSystemTheme) {
|
||||
_followingSystemTheme = true;
|
||||
_followingSystemTheme = true;
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
checkSystemTheme(context);
|
||||
});
|
||||
}
|
||||
// Always check the system theme when this method is called
|
||||
checkSystemTheme(context);
|
||||
|
||||
return I;
|
||||
}
|
||||
|
||||
/// Check and apply the system theme if we're following it
|
||||
///
|
||||
/// This is called automatically when the system brightness changes if
|
||||
/// [followSystemTheme] has been enabled.
|
||||
void checkSystemTheme(BuildContext context) {
|
||||
if (!_followingSystemTheme) return;
|
||||
|
||||
@@ -109,6 +126,7 @@ class ArcaneReactiveTheme extends ArcaneService with WidgetsBindingObserver {
|
||||
final ThemeMode systemMode =
|
||||
systemBrightness == Brightness.dark ? ThemeMode.dark : ThemeMode.light;
|
||||
|
||||
// Only update and notify if the theme actually changed
|
||||
if (systemMode != _currentTheme) {
|
||||
_updateTheme(systemMode);
|
||||
notifyListeners();
|
||||
@@ -145,6 +163,10 @@ class ArcaneReactiveTheme extends ArcaneService with WidgetsBindingObserver {
|
||||
return I;
|
||||
}
|
||||
|
||||
/// Resets the theme service to its default state.
|
||||
///
|
||||
/// This resets both light and dark themes to their default values and
|
||||
/// disables system theme following.
|
||||
@visibleForTesting
|
||||
void reset() {
|
||||
_darkTheme.value = ThemeData.dark();
|
||||
@@ -154,6 +176,7 @@ class ArcaneReactiveTheme extends ArcaneService with WidgetsBindingObserver {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Updates the current theme mode and broadcasts the change.
|
||||
void _updateTheme(ThemeMode themeMode) {
|
||||
_currentTheme = themeMode;
|
||||
_themeStreamController.add(themeMode);
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import "package:arcane_framework/src/services/reactive_theme/reactive_theme_service.dart";
|
||||
import "package:flutter/material.dart";
|
||||
|
||||
class ArcaneTheme extends InheritedWidget {
|
||||
final ThemeMode themeMode;
|
||||
|
||||
const ArcaneTheme({
|
||||
required this.themeMode,
|
||||
required super.child,
|
||||
super.key,
|
||||
});
|
||||
|
||||
static ArcaneTheme? of(BuildContext context) {
|
||||
return context.dependOnInheritedWidgetOfExactType<ArcaneTheme>();
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(ArcaneTheme oldWidget) {
|
||||
return oldWidget.themeMode != themeMode;
|
||||
}
|
||||
|
||||
static ArcaneReactiveTheme get service => ArcaneReactiveTheme.I;
|
||||
static bool get isFollowingSystemTheme => service.isFollowingSystemTheme;
|
||||
static Stream<ThemeMode> get currentThemeStream => service.currentThemeStream;
|
||||
static ThemeMode get currentTheme => service.currentTheme;
|
||||
static ThemeMode get systemTheme => service.systemTheme;
|
||||
static ThemeData get dark => service.dark;
|
||||
static ValueNotifier<ThemeData> get darkTheme => service.darkTheme;
|
||||
static ThemeData get light => service.light;
|
||||
static ValueNotifier<ThemeData> get lightTheme => service.lightTheme;
|
||||
|
||||
static ArcaneReactiveTheme Function({ThemeMode? themeMode}) get switchTheme =>
|
||||
service.switchTheme;
|
||||
static ArcaneReactiveTheme Function(BuildContext context)
|
||||
get followSystemTheme => service.followSystemTheme;
|
||||
static void get checkSystemTheme => service.checkSystemTheme;
|
||||
static ArcaneReactiveTheme Function(ThemeData theme) get setDarkTheme =>
|
||||
service.setDarkTheme;
|
||||
static ArcaneReactiveTheme Function(ThemeData theme) get setLightTheme =>
|
||||
service.setLightTheme;
|
||||
}
|
||||
Reference in New Issue
Block a user