import "package:arcane_framework/arcane_framework.dart"; import "package:collection/collection.dart"; import "package:flutter/widgets.dart"; /// A provider that makes a list of `ArcaneService` instances available to the widget tree. /// /// This class extends `InheritedNotifier` and allows `ArcaneService` instances to be /// accessed throughout the widget tree by descendant widgets. It should be used to /// provide service instances that are shared across the application. /// /// Example: /// ```dart /// ArcaneServiceProvider( /// serviceInstances: [myService], /// child: MyApp(), /// ); /// ``` /// To access the provided services: /// ```dart /// final myService = ArcaneServiceProvider.of(context); /// ``` class ArcaneServiceProvider extends InheritedNotifier>> { /// A list of `ArcaneService` instances available through the provider. List 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. ArcaneServiceProvider({ required List serviceInstances, required super.child, super.key, }) : super( notifier: ValueNotifier>(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(); } /// Retrieves the nearest `ArcaneServiceProvider` 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, ) { 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(context); /// ``` static T? serviceOfType(BuildContext context) { final provider = maybeOf(context); if (provider == null) return null; return provider.serviceInstances.whereType().firstOrNull; } /// Updates the service instances in this provider. /// /// This will trigger a rebuild of all widgets that depend on this provider. void setServices(List 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 newList = List.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 methods for retrieving services in various ways. /// /// Example usage: /// ```dart /// final myService = context.service(); /// ``` 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`. /// /// Example: /// ```dart /// final myService = context.service(); /// ``` T? service() { // First check built-in services final builtInService = Arcane.services.whereType().firstOrNull; if (builtInService != null) return builtInService; // Then check provider return ArcaneServiceProvider.serviceOfType(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 = context.requiredService(); /// ``` T requiredService() { final service = this.service(); assert(service != null, "No service of type $T found"); return service!; } /// Legacy method to maintain backward compatibility. /// /// Prefer using `service()` instead. @Deprecated("Use service() instead") T? serviceOfType() => service(); } /// 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 `service` /// method on `BuildContext`. abstract class ArcaneService with ChangeNotifier, ArcaneServiceLocators {} mixin ArcaneServiceLocators { /// 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(context); /// ``` static T? of(BuildContext context) => context.service(); /// 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(context); /// ``` static T requiredOf(BuildContext context) => context.requiredService(); }