[UNTESTED] Fixes notifiers and adds some additional methods. Adds tests.

Changes:
// ArcaneEnvironment
breaking: context.read<ArcaneEnvironment>() -> ArcaneEnvironment.of(context)
breaking: context.read<ArcaneEnvironment>().state -> ArcaneEnvironment.of(context).environment;

// Feature flag service
added: reset()

// Logging service
added: registerInterface()
added: unregisterInterfaces()
added: unregisterAllInterfaces()

// ArcaneReactiveTheme
fixed: currentMode, dark, light now actually emit new values when changed
added: getters for lightTheme, darkTheme, and systemTheme
TODO: test systemTheme

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2025-04-04 17:08:06 +02:00
parent b129639f1f
commit ac82e93b9d
16 changed files with 1233 additions and 89 deletions
+91 -36
View File
@@ -1,54 +1,109 @@
import "package:arcane_framework/arcane_framework.dart";
import "package:flutter/widgets.dart";
import "package:flutter_bloc/flutter_bloc.dart";
/// A `Cubit` that manages the application environment state.
/// An `InheritedWidget` that provides access to the application environment.
///
/// The `ArcaneEnvironment` cubit holds the current environment (`debug` or `normal`)
/// and provides a method to enable debug mode.
class ArcaneEnvironment extends Cubit<Environment> {
/// Initializes the cubit with the `normal` environment as the default state.
ArcaneEnvironment() : super(Environment.normal);
/// The `ArcaneEnvironment` widget holds the current environment (`debug` or `normal`)
/// and allows descendant widgets to access it.
class ArcaneEnvironment extends InheritedWidget {
/// The current application environment.
final Environment environment;
final ValueChanged<Environment> onEnvironmentChanged;
/// Creates an `ArcaneEnvironment` widget.
const ArcaneEnvironment({
required this.environment,
required Widget child,
required this.onEnvironmentChanged,
Key? key,
}) : super(key: key, child: child);
/// Retrieves the `ArcaneEnvironment` instance from the nearest ancestor.
///
/// Returns `null` if no `ArcaneEnvironment` ancestor is found.
static ArcaneEnvironment? maybeOf(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ArcaneEnvironment>();
}
/// Retrieves the `ArcaneEnvironment` instance from the nearest ancestor.
///
/// Throws a `StateError` if no `ArcaneEnvironment` ancestor is found.
static ArcaneEnvironment of(BuildContext context) {
final ArcaneEnvironment? result = maybeOf(context);
if (result == null) {
throw StateError("No ArcaneEnvironment found in context");
}
return result;
}
@override
bool updateShouldNotify(ArcaneEnvironment oldWidget) {
return environment != oldWidget.environment;
}
void enableDebugMode() => onEnvironmentChanged(Environment.debug);
void disableDebugMode() => onEnvironmentChanged(Environment.normal);
}
/// A `StatefulWidget` that manages and provides the `ArcaneEnvironment`.
///
/// This widget holds the internal state of the environment and rebuilds
/// its descendants when the environment changes.
class ArcaneEnvironmentProvider extends StatefulWidget {
/// The child widget that will have access to the `ArcaneEnvironment`.
final Widget child;
/// The initial environment state. Defaults to `Environment.normal`.
final Environment environment;
/// Creates an `ArcaneEnvironmentProvider`.
const ArcaneEnvironmentProvider({
required this.child,
Key? key,
this.environment = Environment.normal,
}) : super(key: key);
@override
State<ArcaneEnvironmentProvider> createState() =>
_ArcaneEnvironmentProviderState();
}
class _ArcaneEnvironmentProviderState extends State<ArcaneEnvironmentProvider> {
late Environment _environment;
@override
void initState() {
super.initState();
_environment = widget.environment;
}
/// Enables debug mode by setting the environment to `Environment.debug`.
void enableDebugMode() {
if (state == Environment.debug) return;
emit(Environment.debug);
if (_environment == Environment.debug) return;
setState(() {
_environment = Environment.debug;
});
}
/// Disables debug mode by setting the environment to `Environment.normal`.
void disableDebugMode() {
if (state == Environment.normal) return;
emit(Environment.normal);
if (_environment == Environment.normal) return;
setState(() {
_environment = Environment.normal;
});
}
}
/// A widget that provides `ArcaneEnvironment` to the widget tree using `BlocProvider`.
///
/// This widget wraps around a child widget and makes `ArcaneEnvironment` available
/// to the rest of the widget tree. It should be used in combination with `BlocProvider`
/// from the `flutter_bloc` package.
///
/// Example:
/// ```dart
/// ArcaneEnvironmentProvider(
/// child: MyApp(),
/// );
/// ```
class ArcaneEnvironmentProvider extends StatelessWidget {
/// The widget that will be provided with access to the `ArcaneEnvironment`.
final Widget child;
/// Constructs an `ArcaneEnvironmentProvider` with the given [child].
const ArcaneEnvironmentProvider({required this.child, super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => ArcaneEnvironment(),
child: child,
return ArcaneEnvironment(
environment: _environment,
onEnvironmentChanged: (Environment environment) {
setState(() {
_environment = environment;
});
},
child: widget.child,
);
}
}
+4 -6
View File
@@ -53,14 +53,12 @@ class ArcaneServiceProvider extends InheritedNotifier {
/// final provider = ArcaneServiceProvider.of(context);
/// ```
static ArcaneServiceProvider of(BuildContext context) {
final ArcaneServiceProvider? result =
context.dependOnInheritedWidgetOfExactType<ArcaneServiceProvider>();
if (result == null) {
try {
return context
.dependOnInheritedWidgetOfExactType<ArcaneServiceProvider>()!;
} catch (e) {
throw Exception("ArcaneServiceProvider not found in context");
}
return result;
}
}