immutable_di
A lightweight, immutable dependency injection container for Dart.
Usage
1. Define your Container
Create a container by extending DependenciesContainer. It is recommended to pass initialized dependencies via the constructor and map them in the export getter. This ensures your dependencies are fully initialized before the container is used.
import 'package:immutable_di/immutable_di.dart';
class AppDependencies extends DependenciesContainer {
final AuthenticationService authService;
final Database database;
final UserSettings settings;
AppDependencies({
required this.authService,
required this.database,
required this.settings,
});
@override
List<DependencyInfo> get export => [
dependency(authService),
dependency(database),
dependency(settings),
];
}
2. Initialize and Use
Initialize your services and the container at the root of your application (e.g., in main.dart).
void main() async {
// 1. Create instances
final db = Database();
await db.init();
final auth = AuthenticationService(db);
final settings = UserSettings();
// 2. Create the container
final deps = AppDependencies(
database: db,
authService: auth,
settings: settings,
);
// 3. Retrieve dependency using call() or get()
final myAuth = deps<AuthenticationService>();
// or
final myDb = deps.get<Database>();
}
Advanced Patterns
Child Containers (Subsets & Composition)
For complex applications, it is often useful to create smaller, scoped containers for specific features or flows (e.g., an Onboarding flow or a specific screen). These containers can extract dependencies from a parent container and optionally add new, feature-specific dependencies.
Example: Importing Dependencies
You can create a named constructor (e.g., .import) that populates the container from a parent DependenciesContainer.
class FeatureDependencies extends DependenciesContainer {
final AuthenticationService authService;
final UserSettings settings;
// Local dependency specific to this feature
final FeatureController controller;
FeatureDependencies.import(DependenciesContainer parent)
: authService = parent.get<AuthenticationService>(),
settings = parent.get<UserSettings>(),
controller = FeatureController();
@override
List<DependencyInfo> get export => [
dependency(authService),
dependency(settings),
dependency(controller),
];
}
Usage within your application flow:
void startFeature(DependenciesContainer globalDeps) {
final featureDeps = FeatureDependencies.import(globalDeps);
// Now you have a container specifically for this feature
final controller = featureDeps<FeatureController>();
}
Features
- Type Safety: Dependencies are retrieved by their type.
- Lazy Evaluation: The
exportlist is processed and cached upon first access. - Immutable Structure: The container is intended to hold a fixed set of dependencies once initialized.