mirror of
https://github.com/hanskokx/arcane_framework.git
synced 2026-05-14 10:29:06 +02:00
+4
-17
@@ -1,4 +1,5 @@
|
||||
import "package:arcane_framework/arcane_framework.dart";
|
||||
import "package:arcane_framework/src/services/reactive_theme/reactive_theme_switcher.dart";
|
||||
import "package:flutter/material.dart";
|
||||
|
||||
/// A root widget for an Arcane-powered application.
|
||||
@@ -54,22 +55,9 @@ class _ArcaneAppState extends State<ArcaneApp> with WidgetsBindingObserver {
|
||||
return ArcaneEnvironmentProvider(
|
||||
child: ArcaneServiceProvider(
|
||||
serviceInstances: widget.services,
|
||||
child: Builder(
|
||||
child: ArcaneThemeSwitcher(
|
||||
key: _appKey,
|
||||
builder: (BuildContext currentContext) {
|
||||
return StreamBuilder<ThemeMode>(
|
||||
stream: ArcaneReactiveTheme.I.currentThemeStream,
|
||||
initialData: ArcaneReactiveTheme.I.currentTheme,
|
||||
builder: (context, AsyncSnapshot<ThemeMode> snapshot) {
|
||||
final ThemeMode themeMode = snapshot.data ?? ThemeMode.light;
|
||||
|
||||
return ArcaneTheme(
|
||||
themeMode: themeMode,
|
||||
child: widget.child,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: widget.child,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -95,10 +83,9 @@ class _ArcaneAppState extends State<ArcaneApp> with WidgetsBindingObserver {
|
||||
// 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);
|
||||
ArcaneReactiveTheme.I.followSystemTheme(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,3 +19,11 @@ extension DarkMode on BuildContext {
|
||||
return brightness == Brightness.dark;
|
||||
}
|
||||
}
|
||||
|
||||
extension ArcaneThemeContext on BuildContext {
|
||||
/// Get the current theme mode from the nearest ArcaneThemeInherited widget
|
||||
ThemeMode get themeMode {
|
||||
return ArcaneTheme.of(this)?.themeMode ??
|
||||
ArcaneReactiveTheme.I.currentTheme;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,12 +27,16 @@ class ArcaneReactiveTheme extends ArcaneService {
|
||||
|
||||
/// Whether the theme service is currently following the system theme.
|
||||
///
|
||||
/// When true, the theme will automatically switch between light and dark
|
||||
/// 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);
|
||||
final StreamController<ThemeMode> _systemStreamController =
|
||||
StreamController<ThemeMode>.broadcast(
|
||||
onCancel: () {
|
||||
I._systemStreamController.close();
|
||||
},
|
||||
);
|
||||
|
||||
final StreamController<ThemeMode> _themeStreamController =
|
||||
StreamController<ThemeMode>.broadcast(
|
||||
@@ -41,17 +45,20 @@ class ArcaneReactiveTheme extends ArcaneService {
|
||||
},
|
||||
);
|
||||
|
||||
/// Stream of theme mode changes that can be listened to for reactive UI updates.
|
||||
Stream<ThemeMode> get currentThemeStream => I._themeStreamController.stream;
|
||||
/// Stream of `ThemeMode` changes that can be listened to for reactive UI updates.
|
||||
Stream<ThemeMode> get themeChanges => I._themeStreamController.stream;
|
||||
|
||||
/// Returns the `ThemeData` corresponding to the current system theme
|
||||
ThemeMode get systemTheme => _currentSystemTheme;
|
||||
|
||||
/// Tracks the current system theme mode
|
||||
ThemeMode _currentSystemTheme = ThemeMode.system;
|
||||
|
||||
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.
|
||||
ThemeMode get systemTheme => I._systemThemeNotifier.value;
|
||||
|
||||
/// The `ThemeData` for the dark theme.
|
||||
final ValueNotifier<ThemeData> _darkTheme = ValueNotifier(ThemeData.dark());
|
||||
|
||||
@@ -70,14 +77,22 @@ class ArcaneReactiveTheme extends ArcaneService {
|
||||
/// ValueNotifier for the light theme that can be observed for changes.
|
||||
ValueNotifier<ThemeData> get lightTheme => I._lightTheme;
|
||||
|
||||
/// Returns the current `ThemeMode` being used by `ArcaneReactiveTheme`.
|
||||
/// Will automatically update when the theme changes.
|
||||
ThemeMode currentModeOf(BuildContext context) => context.themeMode;
|
||||
|
||||
/// Switches the current theme between light and dark modes.
|
||||
///
|
||||
/// If the theme is currently light, it switches to dark, and vice versa. It also
|
||||
/// notifies listeners to update the UI accordingly.
|
||||
/// If the theme is currently light, it switches to dark, and vice versa. It
|
||||
/// also notifies listeners to update the UI accordingly.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// ArcaneReactiveTheme.I.switchTheme();
|
||||
/// // or
|
||||
/// ArcaneReactiveTheme.I.switchTheme(themeMode: ThemeMode.dark);
|
||||
/// // or
|
||||
/// Arcane.theme.switchTheme(themeMode: ThemeMode.light);
|
||||
/// ```
|
||||
ArcaneReactiveTheme switchTheme({ThemeMode? themeMode}) {
|
||||
_followingSystemTheme = false;
|
||||
@@ -103,40 +118,25 @@ class ArcaneReactiveTheme extends ArcaneService {
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// ArcaneReactiveTheme.I.followSystemTheme(context);
|
||||
/// // or
|
||||
/// Arcane.theme.followSystemTheme(context);
|
||||
/// ```
|
||||
ArcaneReactiveTheme followSystemTheme(BuildContext context) {
|
||||
_followingSystemTheme = true;
|
||||
|
||||
// Always check the system theme when this method is called
|
||||
checkSystemTheme(context);
|
||||
_currentSystemTheme = context.isDarkMode ? ThemeMode.dark : ThemeMode.light;
|
||||
_systemStreamController.add(_currentSystemTheme);
|
||||
_updateTheme(_currentSystemTheme);
|
||||
|
||||
notifyListeners();
|
||||
|
||||
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;
|
||||
|
||||
final Brightness systemBrightness =
|
||||
MediaQuery.platformBrightnessOf(context);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a custom `ThemeData` for the dark theme.
|
||||
///
|
||||
/// This allows you to customize the dark theme and notify listeners to apply the
|
||||
/// changes immediately.
|
||||
/// This allows you to customize the dark theme and notify listeners to apply
|
||||
/// the changes immediately.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
@@ -150,8 +150,8 @@ class ArcaneReactiveTheme extends ArcaneService {
|
||||
|
||||
/// Sets a custom `ThemeData` for the light theme.
|
||||
///
|
||||
/// This allows you to customize the light theme and notify listeners to apply the
|
||||
/// changes immediately.
|
||||
/// This allows you to customize the light theme and notify listeners to apply
|
||||
/// the changes immediately.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import "dart:async";
|
||||
|
||||
import "package:arcane_framework/arcane_framework.dart";
|
||||
import "package:flutter/material.dart";
|
||||
|
||||
class ArcaneThemeSwitcher extends StatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const ArcaneThemeSwitcher({
|
||||
required this.child,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ArcaneThemeSwitcher> createState() => _ArcaneThemeSwitcherState();
|
||||
}
|
||||
|
||||
class _ArcaneThemeSwitcherState extends State<ArcaneThemeSwitcher> {
|
||||
late final StreamSubscription<ThemeMode> _themeModeSubscription;
|
||||
ThemeMode _currentThemeMode = ArcaneReactiveTheme.I.currentTheme;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_themeModeSubscription =
|
||||
ArcaneReactiveTheme.I.themeChanges.listen((ThemeMode newMode) {
|
||||
if (mounted)
|
||||
setState(() {
|
||||
_currentThemeMode = newMode;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_themeModeSubscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ArcaneTheme(
|
||||
themeMode: _currentThemeMode,
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import "dart:async";
|
||||
|
||||
import "package:arcane_framework/src/services/reactive_theme/reactive_theme_service.dart";
|
||||
import "package:flutter/material.dart";
|
||||
|
||||
@@ -16,12 +18,12 @@ class ArcaneTheme extends InheritedWidget {
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(ArcaneTheme oldWidget) {
|
||||
return oldWidget.themeMode != themeMode;
|
||||
return themeMode != oldWidget.themeMode;
|
||||
}
|
||||
|
||||
static ArcaneReactiveTheme get service => ArcaneReactiveTheme.I;
|
||||
static bool get isFollowingSystemTheme => service.isFollowingSystemTheme;
|
||||
static Stream<ThemeMode> get currentThemeStream => service.currentThemeStream;
|
||||
static Stream<ThemeMode> get themeChanges => service.themeChanges;
|
||||
static ThemeMode get currentTheme => service.currentTheme;
|
||||
static ThemeMode get systemTheme => service.systemTheme;
|
||||
static ThemeData get dark => service.dark;
|
||||
@@ -33,7 +35,6 @@ class ArcaneTheme extends InheritedWidget {
|
||||
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 =>
|
||||
|
||||
Reference in New Issue
Block a user