orbital_injector
Default OrbitalScope runtime for the Orbital ecosystem.
What this package contains
orbital_injector provides OrbitalContainer, the concrete scope-tree and
dependency-graph implementation used by the examples and the orbital bundle.
It handles:
- binding registration
- eager and lazy creation
- sync and async resolution
- keyed lookups
- child-scope inheritance
- lifecycle hooks
- deterministic disposal
Quick start
final container = OrbitalContainer.createRoot();
container.register(
OrbitalBinding.singletonAsync<ApiClient>((resolver) async {
return ApiClient.connect();
}),
);
container.register(
OrbitalBinding.singleton<DashboardController>(
(resolver) => DashboardController(
resolver.get<ApiClient>(),
),
dependsOn: [OrbitalDependencyRef.of<ApiClient>()],
),
);
await container.initialize();
final controller = container.get<DashboardController>();
dependsOn vs resolver
dependsOn declares readiness order. It does not inject values into the
factory signature.
Factories always read actual values through OrbitalResolver.
That distinction matters because a sync controller may safely depend on an async service once the scope has already initialized the graph.
Scope tree behavior
OrbitalContainer supports parent/child scopes:
- local registrations are checked first
- parents act as fallback
- child scopes are disposed before parents
- instances are disposed in reverse creation order
This makes ownership explicit and predictable for module/page runtimes such as
orbital_router.
Lifecycle and recovery
If a lifecycle hook fails while a cached instance is becoming ready, the container discards that failed cached instance so a later resolution can retry.
Teardown runs in a fixed order, and each step is isolated so a failure or
timeout in one does not block the others: a custom binding-level dispose
callback (if any), then any cleanup registered via addDisposer(...), then
onDispose().
Best-effort reads with getOrNull
getOrNull<T>() is a non-throwing read for registered bindings:
- returns
nullwhen the binding is not registered, is not ready yet, or failed its lifecycle - returns
nullwhen a registered binding cannot resolve because adependsOnprerequisite is unregistered or the declared graph is cyclic — a broken graph still counts as "missing" for the best-effort read - only genuinely synchronous construction/factory errors still propagate, so a real misconfiguration is not silently masked
More detail
See doc.md for initialization scheduling, readiness semantics, child-scope disposal and failure behavior.