Creating

The Scope API uses a builder pattern allowing you to inject any number of strictly typed values.

Use the lints package to improve your code quality or lint_hard package if you want to grow hair on your chest.

You can then 'use' the injected values from any method called from within the context of the Scope.

import 'package:scope/scope.dart';

import 'my_scope_keys.dart';

void main() 
{
    Scope('main')
    ..value<int>(ageKey, 18)
    ..value<String>(nameKey, 'brett')
    ..run(() {
        someMethod();
    });
}
    
void someMethod() {
    // get values stored in the scope
    var age = use(ageKey);
    var name = use(nameKey);
    print('name: $name, age: $age');
}

In the above example we inject two values:

  • an int with a key 'ageKey' and a value 18.

  • a String with a key 'nameKey' and a value of 'brett'.

The age and name values are retrieved using the use method and the ScopeKey they where injected with.

You will notice that we pass the string 'main' to the Scope. This is an optional argument debugName. We recommend that you always pass a debugName as it is included when an Scope related exception is thrown and makes it easier to identify the source of a problem.

ScopeKey

To inject and use a value you must create a typed key for each value using a ScopeKey:

// my_scope_keys.dart
final ageKey = ScopeKey<int>('ageKey');
final nameKey = ScopeKey<String>('nameKey');
final monthKey = ScopeKey<String>();

As the ScopeKey is typed the values returned from the use call are also correctly typed.

ScopeKeys are declared globally and it's is standard practice to place you Keys in a separate dart library as they need to be available at both the injection site and the use site.

You will notice that with the first two examples we provide the optional string argument debugName. We recommend that you always pass a debugName as it is included when an ScopeKey related exception is thrown and makes it easier to identify the source of a problem.

Example

We start by defining ScopeKeys for each value we want to inject.

final greetingKey = ScopeKey<String>();
final emphasisKey = ScopeKey<String>()
final dbKey = ScopeKey<Db>();

When then create a client (the Greeter class) that will 'use' values injected into the Scope.

class Greeter {
  Greeter()
      : greeting = use(greetingKey),
        emphasis = use(emphasisKey);

  final String greeting;
  final int emphasis;

  void greet(String name) {
    print('$greeting, $name${"!" * emphasis}');
    use(dbKey).createUser(name);
  }
}

Values are made available by creating aScope and injecting each of key/value pair into the Scope.

Once we have injected values into the Scope we call the Scope's run method.

Any method called directly or indirectly from within the run method has access to each of the values injected into the Scope.

import 'package:scope/scope.dart';
void main() {
  Scope()
    ..value<String>(greetingKey, 'Hello')
    ..value<int>(emphasisKey, 3)
    ..run(() {
    // The greet method calls `use` to retrieve the registered values
    Greeter().greet('world'); // 'Hello, world!!!'
  });

  Greeter().greet('you'); // throws: `MissingDependencyException`, because 
                          // `use()` is called outside the `Scope.run` method.
                          // The previously registered values arn't available
                          // outside of the scope of the run method.
}

You can create a Scope anywhere in your code that it might be useful.

Last updated