Scoped DI
In this library, scoped dependency injection (DI) refers to injecting providers that are confined to a specific scope.
To make things clearer in this section, let’s take a look at two of the providers from the previous page.
final numberProvider = Provider((context) => 5);
final doubleNumberPlusArgProvider = Provider.withArgument((context, int arg) { final number = numberProvider.of(context); return number * 2 + arg;});How to scope
Scoping means that the provider must be specified within a ProviderScope before it can be injected.
In case the provider does not take an argument, we scope it the following way:
ProviderScope( providers: [numberProvider] child: // ...)In case the provider takes an argument, we need to specify it when providing it.
ProviderScope( providers: [doubleNumberPlusArgProvider(10)] child: // ...)How to inject
Injecting is the act of retrieving a dependency. It is done with the methods of(context) and maybeOf(context), the latter one being safer because it returns null instead of throwing if the provider is not found in any scopes.
In full example below, we are going to inject the two providers above with numberProvider.of(context) and doubleNumberPlusArgProvider.of(context).
Full example
Try and guess what the displayed text will be before reading the solution.
runApp( MaterialApp( home: Scaffold( body: ProviderScope( providers: [numberProvider, doubleNumberPlusArgProvider], child: Builder( builder: (context) { final number = numberProvider.of(context); final doubleNumberPlusArg = doubleNumberPlusArgProvider.of(context); return Text('$number $doubleNumberPlusArg'); }, ), ), ), ),);The solution is “5 20”.
Scoping correctly with context
Some providers might have a dependency on other providers, to handle such cases, you can:
- Access another provider if it is declared in an ancestor
ProviderScope. - Declare the dependent provider after the provider it depends on, within the same
ProviderScope. The order of declaration matters here otherwise aProviderForwardReferenceErrorwill be thrown at runtime.
Wrong example
The provider doubleNumberPlusArgProvider depends on numberProvider. Therefore, when scoping them both in the same ProviderScope, numberProvider must be declared first.
Therefore, the following code will lead to a ProviderForwardReferenceError:
ProviderScope( providers: [ doubleNumberPlusArgProvider(10), numberProvider, ], child: // ...)The error can be prevented by reordering the list of providers:
ProviderScope( providers: [ numberProvider, doubleNumberPlusArgProvider(10), ], child: // ...)Graphical representation
When you inject a provider, you need to ensure that one of the ancestors of the widget — where the injection takes place — is a ProviderScope providing that provider. If this is not the case, an error will be thrown at runtime.
Refer to the graph below to understand the problem.
If you lift the provider scope up to the parent of the widget, the injection will work as expected.