Skip to content

Quick start ​

This page builds a two-service program in about five minutes. You'll register services with an injector and then ask it for one, letting it build the dependencies for you.

1. Write some services ​

Every service needs two things:

  • a factory, which says this is how to build it, and
  • a provider, which says an injector makes this service available.

The recommended convention is to keep both on the service itself, as static Factory and DefaultProvider members. Your services stay plain Dart classes that don't extend or implement anything.

dart
class Logger {
  // How to build a Logger.
  static final Factory = InjectableFactory.withoutDependencies<Logger>(
    () => Logger(),
  );

  // How to make a Logger available in an injector.
  static final DefaultProvider = Provider(Logger);

  void log(String message) => print('[log] $message');
}

class GreetingService {
  GreetingService(this._logger);

  static final Factory = InjectableFactory.withDependencies<GreetingService>(
    DependencyDataBuilder.list([Logger]),
    (deps) => GreetingService(deps.get<Logger>()),
  );

  static final DefaultProvider = Provider(GreetingService);

  final Logger _logger;

  String greet(String name) {
    _logger.log('greeting $name');
    return 'Hello, $name!';
  }
}

DependencyDataBuilder.list([Logger]) tells the injector that GreetingService needs a Logger. The injector resolves it before calling the factory, and deps.get<Logger>() hands it over.

Lint note

Factory and DefaultProvider are capitalized on purpose so they read like part of the type. The lints package flags that with non_constant_identifier_names, so turn that rule off in analysis_options.yaml:

yaml
analyzer:
  errors:
    non_constant_identifier_names: ignore

2. Create an injector ​

Registration now reads as a list of services:

dart
// 1. Say which services this injector makes available.
final injector = Injector([
  Logger.DefaultProvider,
  GreetingService.DefaultProvider,
]);

// 2. Register how to build each one.
injector.registerFactories([
  Logger.Factory,
  GreetingService.Factory,
]);

3. Ask for a service ​

getService returns a Future, because any service in the chain may be asynchronous.

dart
// 3. Ask for a service. Its dependencies are built first.
final greeter = await injector.getService<GreetingService>();
final message = greeter.greet('Dart');
print(message); // Hello, Dart!

The Logger is built first, then the GreetingService. Both are cached in the injector, so the next getService call returns the same instances.

The whole program ​

Show full source
dart
import 'package:complete_dependency_injector/complete_dependency_injector.dart';

class Logger {
  // How to build a Logger.
  static final Factory = InjectableFactory.withoutDependencies<Logger>(
    () => Logger(),
  );

  // How to make a Logger available in an injector.
  static final DefaultProvider = Provider(Logger);

  void log(String message) => print('[log] $message');
}

class GreetingService {
  GreetingService(this._logger);

  static final Factory = InjectableFactory.withDependencies<GreetingService>(
    DependencyDataBuilder.list([Logger]),
    (deps) => GreetingService(deps.get<Logger>()),
  );

  static final DefaultProvider = Provider(GreetingService);

  final Logger _logger;

  String greet(String name) {
    _logger.log('greeting $name');
    return 'Hello, $name!';
  }
}

Future<String> runQuickStart() async {
  // 1. Say which services this injector makes available.
  final injector = Injector([
    Logger.DefaultProvider,
    GreetingService.DefaultProvider,
  ]);

  // 2. Register how to build each one.
  injector.registerFactories([
    Logger.Factory,
    GreetingService.Factory,
  ]);

  // 3. Ask for a service. Its dependencies are built first.
  final greeter = await injector.getService<GreetingService>();
  final message = greeter.greet('Dart');
  print(message); // Hello, Dart!

  await injector.dispose();
  return message;
}

void main() => runQuickStart();

Where to go next ​

Released under the MIT License.