mirror of
https://github.com/hanskokx/arcane_framework.git
synced 2026-05-14 02:19:08 +02:00
Update service provider
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -29,6 +29,8 @@ jobs:
|
|||||||
flutter-version: ${{ env.PURO_FLUTTER_VERSION == 'stable' && '' || env.FLUTTER_VERSION }}
|
flutter-version: ${{ env.PURO_FLUTTER_VERSION == 'stable' && '' || env.FLUTTER_VERSION }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: flutter pub get
|
run: flutter pub get
|
||||||
|
- name: Run build_runner
|
||||||
|
run: dart run build_runner build -d
|
||||||
- name: Analyze
|
- name: Analyze
|
||||||
run: flutter analyze
|
run: flutter analyze
|
||||||
- name: Test
|
- name: Test
|
||||||
|
|||||||
+8
-1
@@ -15,7 +15,14 @@
|
|||||||
|
|
||||||
### ArcaneServiceProvider
|
### ArcaneServiceProvider
|
||||||
|
|
||||||
- [BREAKING] `ArcaneServiceProvider.of(context)` now returns a nullable instance, rather than throwing an exception.
|
- [NEW] Added a new `ArcaneServiceProvider.maybeOf(context)` getter which returns a nullable `ArcaneServiceProvider` instance.
|
||||||
|
- [NEW] `ArcaneServiceProvider` now includes a `serviceOfType<T>(context)` getter to retrieve a nullable registered service instance.
|
||||||
|
- [NEW] An `addService` method was added to `ArcaneServiceProvider`
|
||||||
|
- [NEW] A `setServices` method was added to `ArcaneServiceProvider`. Invoking this method with a list of `ArcaneService` instances will replace all existing services in the `ArcaneServiceProvider`.
|
||||||
|
- [DEPRECATED] `context.serviceOfType<T>` has been deprecated in favor of `context.service<T>`.
|
||||||
|
- [NEW] `context.requiredService<T>` has been added to provide a mechanism for ensuring a particular service has been registered.
|
||||||
|
- [NEW] `ArcaneService<T>.of(context)` has been added for easy access to service instances. It returns a nullable service instance.
|
||||||
|
- [NEW] `ArcaneService<T>.requiredOf(context)` has been added. It returns a non-nullable service instance, and throws an exception if the service instance has not been registered.
|
||||||
|
|
||||||
### Authentication Service (ArcaneAuth)
|
### Authentication Service (ArcaneAuth)
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,8 @@ To use Arcane Framework in your Dart or Flutter project, follow these steps:
|
|||||||
|
|
||||||
1. Add the dependency to your pubspec.yaml:
|
1. Add the dependency to your pubspec.yaml:
|
||||||
|
|
||||||
```yaml
|
```shell
|
||||||
dependencies:
|
flutter pub add arcane_framework
|
||||||
arcane_framework: <latest>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Wrap your `MaterialApp` or `CupertinoApp` with the `ArcaneApp` Widget, providing the necessary services and your root widget.
|
2. Wrap your `MaterialApp` or `CupertinoApp` with the `ArcaneApp` Widget, providing the necessary services and your root widget.
|
||||||
@@ -36,7 +35,7 @@ To use Arcane Framework in your Dart or Flutter project, follow these steps:
|
|||||||
services: [
|
services: [
|
||||||
MyArcaneService.I,
|
MyArcaneService.I,
|
||||||
],
|
],
|
||||||
child: MyApp(...),
|
child: MainApp(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -74,7 +73,6 @@ class FavoriteColorService extends ArcaneService {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
To register a service with Arcane, simply add the instance of the `ArcaneService` to your list of services when initializing the `ArcaneApp`.
|
To register a service with Arcane, simply add the instance of the `ArcaneService` to your list of services when initializing the `ArcaneApp`.
|
||||||
@@ -84,7 +82,7 @@ ArcaneApp(
|
|||||||
services: [
|
services: [
|
||||||
FavoriteColorService.I,
|
FavoriteColorService.I,
|
||||||
],
|
],
|
||||||
child: MyApp(...),
|
child: MainApp(),
|
||||||
),
|
),
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -17,89 +17,171 @@ import "package:flutter/widgets.dart";
|
|||||||
/// ```
|
/// ```
|
||||||
/// To access the provided services:
|
/// To access the provided services:
|
||||||
/// ```dart
|
/// ```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.
|
/// 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.
|
/// 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.
|
/// The [child] widget will be the root of the widget subtree that has access to the services.
|
||||||
@override
|
ArcaneServiceProvider({
|
||||||
const ArcaneServiceProvider({
|
required List<ArcaneService> serviceInstances,
|
||||||
required this.serviceInstances,
|
|
||||||
required super.child,
|
required super.child,
|
||||||
super.key,
|
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.
|
/// Retrieves the nearest `ArcaneServiceProvider` in the widget tree.
|
||||||
///
|
///
|
||||||
/// This method is used to access the `ArcaneServiceProvider` and its provided services
|
/// Throws an assertion error if no provider is found.
|
||||||
/// from any descendant widget. It throws an exception if no `ArcaneServiceProvider`
|
|
||||||
/// is found in the widget tree.
|
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```dart
|
/// ```dart
|
||||||
/// final provider = ArcaneServiceProvider.of(context);
|
/// final provider = ArcaneServiceProvider.of(context);
|
||||||
/// ```
|
/// ```
|
||||||
static ArcaneServiceProvider? of(BuildContext context) {
|
static ArcaneServiceProvider of<T extends ArcaneService>(
|
||||||
return context.dependOnInheritedWidgetOfExactType<ArcaneServiceProvider>();
|
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
|
/// An extension on `BuildContext` to provide easy access to `ArcaneService` instances
|
||||||
/// that are registered in an `ArcaneServiceProvider`.
|
/// that are registered in an `ArcaneServiceProvider`.
|
||||||
///
|
///
|
||||||
/// This extension provides a `serviceOfType` method, which searches for a specific
|
/// This extension provides methods for retrieving services in various ways.
|
||||||
/// service of type `T` in the current `ArcaneServiceProvider` or in the list of built-in
|
|
||||||
/// services.
|
|
||||||
///
|
///
|
||||||
/// Example usage:
|
/// Example usage:
|
||||||
/// ```dart
|
/// ```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
|
/// 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`).
|
/// in the `ArcaneServiceProvider` or in the list of built-in services (`Arcane.services`).
|
||||||
///
|
///
|
||||||
/// If no such service is found, it returns `null`.
|
/// 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:
|
/// Example:
|
||||||
/// ```dart
|
/// ```dart
|
||||||
/// final MyService? myService = context.serviceOfType<MyService>();
|
/// final myService = context.requiredService<MyService>();
|
||||||
/// ```
|
/// ```
|
||||||
T? serviceOfType<T extends ArcaneService>() {
|
T requiredService<T extends ArcaneService>() {
|
||||||
final T? builtInService =
|
final service = this.service<T>();
|
||||||
Arcane.services.firstWhereOrNull((s) => s.runtimeType == T) as T?;
|
assert(service != null, "No service of type $T found");
|
||||||
|
return service!;
|
||||||
if (builtInService != null) return builtInService;
|
|
||||||
|
|
||||||
final T? foundService =
|
|
||||||
dependOnInheritedWidgetOfExactType<ArcaneServiceProvider>()
|
|
||||||
?.serviceInstances
|
|
||||||
.firstWhereOrNull((s) => s.runtimeType == T) as T?;
|
|
||||||
return foundService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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.
|
/// An abstract class representing a service in the Arcane architecture.
|
||||||
///
|
///
|
||||||
/// Classes that extend `ArcaneService` can use `ChangeNotifier` functionality
|
/// Classes that extend `ArcaneService` can use `ChangeNotifier` functionality
|
||||||
/// to notify listeners of changes. Services are typically registered in
|
/// 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`.
|
/// 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) =>
|
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>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ void main() {
|
|||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final provider = ArcaneServiceProvider.of(context);
|
final provider = ArcaneServiceProvider.of(context);
|
||||||
expect(provider?.serviceInstances, equals(testServices));
|
expect(provider.serviceInstances, equals(testServices));
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -28,13 +28,122 @@ void main() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("serviceOfType extension returns correct service",
|
testWidgets("static serviceOfType<T> method returns correct service",
|
||||||
(tester) async {
|
(tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
ArcaneApp(
|
ArcaneApp(
|
||||||
services: testServices,
|
services: testServices,
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
|
final service =
|
||||||
|
ArcaneServiceProvider.serviceOfType<MockArcaneService>(
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
expect(service, isNotNull);
|
||||||
|
expect(service, isA<MockArcaneService>());
|
||||||
|
return const SizedBox();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
"static serviceOfType<T> method returns correct service and returns null when not found",
|
||||||
|
(tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
ArcaneApp(
|
||||||
|
services: testServices,
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
// Should find this service
|
||||||
|
final service =
|
||||||
|
ArcaneServiceProvider.serviceOfType<MockArcaneService>(
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(service, isA<MockArcaneService>());
|
||||||
|
|
||||||
|
// Returns null for unregistered services
|
||||||
|
expect(
|
||||||
|
ArcaneServiceProvider.serviceOfType<UnregisteredService>(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
isNull,
|
||||||
|
);
|
||||||
|
|
||||||
|
return const SizedBox();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets("service<T> extension returns correct service", (tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
ArcaneApp(
|
||||||
|
services: testServices,
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final service = context.service<MockArcaneService>();
|
||||||
|
expect(service, isNotNull);
|
||||||
|
expect(service, isA<MockArcaneService>());
|
||||||
|
return const SizedBox();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
"requiredService<T> extension returns correct service and throws when not found",
|
||||||
|
(tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
ArcaneApp(
|
||||||
|
services: testServices,
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
// Should find this service
|
||||||
|
final service = context.requiredService<MockArcaneService>();
|
||||||
|
expect(service, isA<MockArcaneService>());
|
||||||
|
|
||||||
|
// Should throw for missing service
|
||||||
|
expect(
|
||||||
|
() => context.requiredService<UnregisteredService>(),
|
||||||
|
throwsA(isA<AssertionError>()),
|
||||||
|
);
|
||||||
|
|
||||||
|
return const SizedBox();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets("service<T> returns null for unregistered service",
|
||||||
|
(tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
ArcaneApp(
|
||||||
|
services: testServices,
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final service = context.service<UnregisteredService>();
|
||||||
|
expect(service, isNull);
|
||||||
|
return const SizedBox();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets("legacy serviceOfType method still works but is deprecated",
|
||||||
|
(tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
ArcaneApp(
|
||||||
|
services: testServices,
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
final service = context.serviceOfType<MockArcaneService>();
|
final service = context.serviceOfType<MockArcaneService>();
|
||||||
expect(service, isNotNull);
|
expect(service, isNotNull);
|
||||||
expect(service, isA<MockArcaneService>());
|
expect(service, isA<MockArcaneService>());
|
||||||
@@ -45,15 +154,49 @@ void main() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("serviceOfType returns null for unregistered service",
|
testWidgets("service updates trigger rebuilds", (tester) async {
|
||||||
(tester) async {
|
late ArcaneServiceProvider provider;
|
||||||
|
int buildCount = 0;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ArcaneServiceProvider(
|
||||||
|
serviceInstances: testServices,
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
provider = ArcaneServiceProvider.of(context);
|
||||||
|
buildCount++;
|
||||||
|
// Access a service to create dependency
|
||||||
|
context.service<MockArcaneService>();
|
||||||
|
return const Text("Test");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(buildCount, 1);
|
||||||
|
|
||||||
|
// Update services and verify rebuild
|
||||||
|
provider.setServices([MockArcaneService(), AnotherMockService()]);
|
||||||
|
await tester.pump();
|
||||||
|
expect(buildCount, 2);
|
||||||
|
|
||||||
|
// Add a service and verify rebuild
|
||||||
|
provider.addService(UnregisteredService());
|
||||||
|
await tester.pump();
|
||||||
|
expect(buildCount, 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets("ArcaneService.of<T> static helper works", (tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
ArcaneApp(
|
ArcaneApp(
|
||||||
services: testServices,
|
services: testServices,
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final service = context.serviceOfType<UnregisteredService>();
|
final service = ArcaneService.of<MockArcaneService>(context);
|
||||||
expect(service, isNull);
|
expect(service, isNotNull);
|
||||||
|
expect(service, isA<MockArcaneService>());
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -61,25 +204,32 @@ void main() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("updateShouldNotify always returns true", (tester) async {
|
testWidgets("ArcaneService.requiredOf<T> static helper works",
|
||||||
final provider = ArcaneServiceProvider(
|
(tester) async {
|
||||||
serviceInstances: testServices,
|
await tester.pumpWidget(
|
||||||
child: const SizedBox(),
|
ArcaneApp(
|
||||||
);
|
services: testServices,
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final service =
|
||||||
|
ArcaneService.requiredOf<MockArcaneService>(context);
|
||||||
|
expect(service, isA<MockArcaneService>());
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
provider.updateShouldNotify(
|
() => ArcaneService.requiredOf<UnregisteredService>(context),
|
||||||
ArcaneServiceProvider(
|
throwsA(isA<AssertionError>()),
|
||||||
serviceInstances: testServices,
|
);
|
||||||
child: const SizedBox(),
|
|
||||||
|
return const SizedBox();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mock classes for testing
|
||||||
class MockArcaneService extends ArcaneService {}
|
class MockArcaneService extends ArcaneService {}
|
||||||
|
|
||||||
class AnotherMockService extends ArcaneService {}
|
class AnotherMockService extends ArcaneService {}
|
||||||
|
|||||||
Reference in New Issue
Block a user