Using
Once you create a
Scope
and inject values
you can access those values from any method called within the context of the Scope.By 'context' we mean any method that sits below the Scope's
run
method on the call stack:import 'package:scope/scope.dart';
void httpRequestHandler(HttpRequest request)
{
/// create a scope
Scope()
..value<HttpRequest>(requestKey, request)
..value<int>(ageKey, 18)
..run(() => a()); /// call a() from within the scope
}
void a() => b();
void b() => c();
void c() {
// c is within the scope, so we can call 'use' to access values
// in the scope.
print('Your are ${use(ageKey)} years old');
print('Your ip is: ${use(requestKey).clientIp}');
}
You can see from the above example that the method
c
was call from b
which was called from a
which was called from the Scope's run
method thus c
is in the Scope's context.Scope.run -> a ->b ->c
So a, b, and c are all within the Scope's context and have access to the Scope's values.
The
use
method provide access to injected values that exist in our scope or any ancestors scope.To obtain an injected value we can call either of the two forms of
use
. The two forms are equivalent and exist simply for convenience. var age = use(ageKey);
var name = Scope.use(ageKey);
The first version is concise whilst the second version provides better documentation.
ScopeKeys are typed and as such the result of
use
is also typed.var ageKey = ScopeKey<int>();
var age = use(ageKey);
age is int;
If you call
use(somekey)
and somekey
hasn't been added to your Scope then a MissingDependencyException
will be thrown.You can avoid this problem using one of the following techniques:
The most elegant method is to use a default.
You can set a default when:
- you create the scopeKey
- you call use
When creating a ScopeKey you can set a default value.
Any time
use
is called for that ScopeKey and the ScopeKey is not in Scope then the default will be returned.The default is fixed for the life of the ScopeKey.
ScopeKey<int> countKey = ScopeKey.withDefault<int>(0);
count = use(countKey);
This is perhaps the most useful method as it allows you to provide a default value from where you call
use
.final count = use(countKey, withDefault: () => nextCount++);
We use a lambda
() =>
for the default value to help with performance. The lambda provided to the default
argument will only be called if the ScopeKey is missing. This allows the default to call a potentially long running method.If the ScopeKey was created using
Scope.withDefault
and you call use
with a default value then the default value provided to use
will take precedence.ScopeKey<int> countKey = ScopeKey.withDefault<int>(0);
count = use(countKey, withDefault: () => 1);
expect(count, equals(1));
Before calling
use
can test if the ScopeKey exists by calling hasScopeKey()
or Scope.hasScopeKey
final int count;
if (hasScopeKey(countKey)) {
count = use(countKey);
} else count = 1;
hasScopeValue works like
hasScopeKey
in that it checks if a key is in scope. The difference is that if the key isn't in scope but has a default value then hasScopeValue
will return true.final countKey = ScopeKey.withDefault<int>(10);
final int count;
if (hasScopeValue(countKey)) {
count = use(countKey);
} else count = 20;
expect(count, equals(10));
You can check if you are within a Scope by calling
isWithinScope()
or Scope.isWithinScope().
This method isn't very reliable as it may turn out that you are running in someone else's scope.
final int count;
if (isWithinScope()) {
count = use(countKey);
}
else count = 1;
Last modified 1yr ago