mirror of
https://github.com/hanskokx/arcane_framework.git
synced 2026-05-14 10:29:06 +02:00
Update service provider
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -17,89 +17,171 @@ import "package:flutter/widgets.dart";
|
||||
/// ```
|
||||
/// To access the provided services:
|
||||
/// ```dart
|
||||
/// final provider = ArcaneServiceProvider.of(context);
|
||||
/// final myService = ArcaneServiceProvider.of<MyService>(context);
|
||||
/// ```
|
||||
class ArcaneServiceProvider extends InheritedNotifier {
|
||||
class ArcaneServiceProvider
|
||||
extends InheritedNotifier<ValueNotifier<List<ArcaneService>>> {
|
||||
/// A list of `ArcaneService` instances available through the provider.
|
||||
final List<ArcaneService> serviceInstances;
|
||||
List<ArcaneService> get serviceInstances => notifier!.value;
|
||||
|
||||
/// Creates an `ArcaneServiceProvider` that provides [serviceInstances] to the widget tree.
|
||||
///
|
||||
/// The [child] widget will be the root of the widget subtree that has access to the services.
|
||||
@override
|
||||
const ArcaneServiceProvider({
|
||||
required this.serviceInstances,
|
||||
ArcaneServiceProvider({
|
||||
required List<ArcaneService> serviceInstances,
|
||||
required super.child,
|
||||
super.key,
|
||||
});
|
||||
}) : super(
|
||||
notifier: ValueNotifier<List<ArcaneService>>(serviceInstances),
|
||||
);
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(covariant ArcaneServiceProvider oldWidget) {
|
||||
return !const DeepCollectionEquality().equals(
|
||||
serviceInstances,
|
||||
oldWidget.serviceInstances,
|
||||
);
|
||||
/// Retrieves the nearest `ArcaneServiceProvider` in the widget tree.
|
||||
///
|
||||
/// Returns null if no provider is found.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final provider = ArcaneServiceProvider.maybeOf(context);
|
||||
/// ```
|
||||
static ArcaneServiceProvider? maybeOf(BuildContext context) {
|
||||
return context.dependOnInheritedWidgetOfExactType<ArcaneServiceProvider>();
|
||||
}
|
||||
|
||||
/// Retrieves the nearest `ArcaneServiceProvider` in the widget tree.
|
||||
///
|
||||
/// This method is used to access the `ArcaneServiceProvider` and its provided services
|
||||
/// from any descendant widget. It throws an exception if no `ArcaneServiceProvider`
|
||||
/// is found in the widget tree.
|
||||
/// Throws an assertion error if no provider is found.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final provider = ArcaneServiceProvider.of(context);
|
||||
/// ```
|
||||
static ArcaneServiceProvider? of(BuildContext context) {
|
||||
return context.dependOnInheritedWidgetOfExactType<ArcaneServiceProvider>();
|
||||
static ArcaneServiceProvider of<T extends ArcaneService>(
|
||||
BuildContext context,
|
||||
) {
|
||||
final provider = maybeOf(context);
|
||||
assert(provider != null, "No ArcaneServiceProvider found in context");
|
||||
return provider!;
|
||||
}
|
||||
|
||||
/// Retrieves a service of type `T` from the nearest provider.
|
||||
///
|
||||
/// Returns null if no service of type `T` is found or if no provider exists.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final myService = ArcaneServiceProvider.of<MyService>(context);
|
||||
/// ```
|
||||
static T? serviceOfType<T extends ArcaneService>(BuildContext context) {
|
||||
final provider = maybeOf(context);
|
||||
if (provider == null) return null;
|
||||
|
||||
return provider.serviceInstances.whereType<T>().firstOrNull;
|
||||
}
|
||||
|
||||
/// Updates the service instances in this provider.
|
||||
///
|
||||
/// This will trigger a rebuild of all widgets that depend on this provider.
|
||||
void setServices(List<ArcaneService> newServices) {
|
||||
notifier?.value = newServices;
|
||||
}
|
||||
|
||||
/// Adds a new service to this provider.
|
||||
///
|
||||
/// If a service of the same type already exists, it will be replaced.
|
||||
void addService(ArcaneService service) {
|
||||
final int existingIndex = serviceInstances.indexWhere(
|
||||
(s) => s.runtimeType == service.runtimeType,
|
||||
);
|
||||
|
||||
final List<ArcaneService> newList =
|
||||
List<ArcaneService>.from(serviceInstances);
|
||||
|
||||
if (existingIndex >= 0) {
|
||||
newList[existingIndex] = service;
|
||||
} else {
|
||||
newList.add(service);
|
||||
}
|
||||
|
||||
notifier?.value = newList;
|
||||
}
|
||||
}
|
||||
|
||||
/// An extension on `BuildContext` to provide easy access to `ArcaneService` instances
|
||||
/// that are registered in an `ArcaneServiceProvider`.
|
||||
///
|
||||
/// This extension provides a `serviceOfType` method, which searches for a specific
|
||||
/// service of type `T` in the current `ArcaneServiceProvider` or in the list of built-in
|
||||
/// services.
|
||||
/// This extension provides methods for retrieving services in various ways.
|
||||
///
|
||||
/// Example usage:
|
||||
/// ```dart
|
||||
/// final MyService? myService = context.serviceOfType<MyService>();
|
||||
/// final myService = context.service<MyService>();
|
||||
/// ```
|
||||
extension ServiceProvider on BuildContext {
|
||||
extension ServiceProviderExtension on BuildContext {
|
||||
/// Finds and returns the `ArcaneService` instance of type `T` that has been registered
|
||||
/// in the `ArcaneServiceProvider` or in the list of built-in services (`Arcane.services`).
|
||||
///
|
||||
/// If no such service is found, it returns `null`.
|
||||
///
|
||||
/// - `T`: The type of the service to be retrieved, which extends `ArcaneService`.
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final myService = context.service<MyService>();
|
||||
/// ```
|
||||
T? service<T extends ArcaneService>() {
|
||||
// First check built-in services
|
||||
final builtInService = Arcane.services.whereType<T>().firstOrNull;
|
||||
if (builtInService != null) return builtInService;
|
||||
|
||||
// Then check provider
|
||||
return ArcaneServiceProvider.serviceOfType<T>(this);
|
||||
}
|
||||
|
||||
/// Finds and returns the `ArcaneService` instance of type `T` that has been registered
|
||||
/// in the `ArcaneServiceProvider` or in the list of built-in services (`Arcane.services`).
|
||||
///
|
||||
/// Throws an assertion error if no service is found.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final MyService? myService = context.serviceOfType<MyService>();
|
||||
/// final myService = context.requiredService<MyService>();
|
||||
/// ```
|
||||
T? serviceOfType<T extends ArcaneService>() {
|
||||
final T? builtInService =
|
||||
Arcane.services.firstWhereOrNull((s) => s.runtimeType == T) as T?;
|
||||
|
||||
if (builtInService != null) return builtInService;
|
||||
|
||||
final T? foundService =
|
||||
dependOnInheritedWidgetOfExactType<ArcaneServiceProvider>()
|
||||
?.serviceInstances
|
||||
.firstWhereOrNull((s) => s.runtimeType == T) as T?;
|
||||
return foundService;
|
||||
T requiredService<T extends ArcaneService>() {
|
||||
final service = this.service<T>();
|
||||
assert(service != null, "No service of type $T found");
|
||||
return service!;
|
||||
}
|
||||
|
||||
/// Legacy method to maintain backward compatibility.
|
||||
///
|
||||
/// Prefer using `service<T>()` instead.
|
||||
@Deprecated("Use service<T>() instead")
|
||||
T? serviceOfType<T extends ArcaneService>() => service<T>();
|
||||
}
|
||||
|
||||
/// An abstract class representing a service in the Arcane architecture.
|
||||
///
|
||||
/// Classes that extend `ArcaneService` can use `ChangeNotifier` functionality
|
||||
/// to notify listeners of changes. Services are typically registered in
|
||||
/// `ArcaneServiceProvider` and can be accessed using the `serviceOfType`
|
||||
/// `ArcaneServiceProvider` and can be accessed using the `service`
|
||||
/// method on `BuildContext`.
|
||||
abstract class ArcaneService<T> with ChangeNotifier {
|
||||
abstract class ArcaneService with ChangeNotifier {
|
||||
/// Retrieves a service of the specified type from the context.
|
||||
///
|
||||
/// Returns null if no service of type `T` is found.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final myService = ArcaneService.of<MyService>(context);
|
||||
/// ```
|
||||
static T? of<T extends ArcaneService>(BuildContext context) =>
|
||||
context.serviceOfType();
|
||||
context.service<T>();
|
||||
|
||||
/// Retrieves a service of the specified type from the context.
|
||||
///
|
||||
/// Throws an assertion error if no service of type `T` is found.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final myService = ArcaneService.requiredOf<MyService>(context);
|
||||
/// ```
|
||||
static T requiredOf<T extends ArcaneService>(BuildContext context) =>
|
||||
context.requiredService<T>();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user