Extremely simple, fast and clear Dependency Injection (DI) and Inversion of Control (IOC) container.

Features

  • Helping resolving dependencies initialisation order easily
  • Pure Dart library, no Flutter dependency
  • Dependency Injection by explicit Type or by Interface
  • "Lazy" instantiating
  • Singletons and Multiple dependencies (see getAll for details)
  • Resolution context with current dependency tree for automatic loggers definition and similar tasks
  • Nested containers with the dependencies overriding
  • Releasing declared bindings together with their cached instances (see release and releaseAll)
  • Easy debug: clear errors messages about missing dependencies with exact dependency tree description

Getting started

Simply add as a dependency in pubspec.yaml

Usage

Please follow to /example folder for simple console application template example.

  final di = DI();
    di
      ..bind(to: (c) => createLogger(c.plan[c.plan.length - 2]), dynamic: true)
      ..bind(to: (c) => AppConfig)
      ..bind(to: (c) => AppController(appConfig: c.get(), logger: c.get()));

Bindings can be dropped at any moment. release removes the binding declaration of the given type and the instance cached for it, so the container simply forgets them — no disposing is performed:

  di.release<AppController>();

Just like get, release expects the type to be declared once. Multiple declarations are dropped all together by releaseAll, the counterpart of getAll:

  di.releaseAll<RequestInterceptor>();

Only the container the method is called on is affected: bindings declared in parent containers are left intact and still resolvable through the hierarchy.

Nested containers

A nested container declares its own bindings and inherits the rest from the parent. The closest declaration wins, so the dependency can be substituted for a single scope without touching the upstream containers — the overridden binding is not instantiated at all:

  final root = DI()..bind<AppConfig>(to: (c) => ProductionAppConfig());
  final sandbox = DI(parent: root)..bind<AppConfig>(to: (c) => SandboxAppConfig());

  sandbox.get<AppConfig>(); // SandboxAppConfig
  root.get<AppConfig>(); // ProductionAppConfig

getAll keeps collecting the whole hierarchy, the closest declarations first, so the multi injection still works across the nested containers.

Releasing is a management operation, not a part of the resolution: calling it from inside an instance factory throws, since the dependency tree being built at the moment would become inconsistent.

Libraries

dicom