minimal_di
A small and simple Dependency Injection (DI) container for Dart/Flutter.
Features
- Clear API for sync/async registrations and lookups
- Lazy Singleton and Factory (both sync and async)
- Default key is
T.toString()(type-based key) - Helpful errors for wrong usage
- Lightweight, no extra dependencies
Install
Add to your pubspec.yaml:
dependencies:
minimal_di: ^0.0.6
Quick Start
import 'package:minimal_di/minimal_di.dart';
void main() async {
final di = Dependency.instance; // or Dependency()
// 1) Singleton (sync) — same DateTime
di.putSingleton<DateTime>(DateTime.now(), named: 'singleton');
final s1 = di.get<DateTime>(named: 'singleton');
final s2 = di.get<DateTime>(named: 'singleton');
assert(identical(s1, s2));
// 2) Lazy Singleton (sync) — same DateTime
di.putLazySingleton<DateTime>(() => DateTime.now(), named: 'lazy');
final l1 = di.get<DateTime>(named: 'lazy');
final l2 = di.get<DateTime>(named: 'lazy'); // same instance
assert(identical(l1, l2));
// 3) Factory (sync) — different DateTime
di.putFactory<DateTime>(() => DateTime.now(), named: 'factory');
final f1 = di.get<DateTime>(named: 'factory');
final f2 = di.get<DateTime>(named: 'factory'); // different each time
assert(!identical(f1, f2));
// 4) Lazy Singleton (async) — same DateTime
di.putAsyncLazySingleton<DateTime>(() async {
await Future.delayed(const Duration(milliseconds: 50));
return DateTime.now();
}, named: 'asyncLazy');
final al1 = await di.getAsync<DateTime>(named: 'asyncLazy');
final al2 = await di.getAsync<DateTime>(named: 'asyncLazy');
assert(identical(al1, al2));
// 5) Factory (async) — different DateTime
di.putAsyncFactory<DateTime>(() async {
await Future.delayed(const Duration(milliseconds: 10));
return DateTime.now();
}, named: 'asyncFactory');
final af1 = await di.getAsync<DateTime>(named: 'asyncFactory');
final af2 = await di.getAsync<DateTime>(named: 'asyncFactory');
assert(!identical(af1, af2));
}
API (Dependency)
- Check:
has<T>({String? named}) -> bool - Replace:
set<T>(T instance, {String? named}) - Register (sync)
putSingleton<T>(T instance, {String? named})putLazySingleton<T>(T Function() func, {String? named})putFactory<T>(T Function() func, {String? named})
- Register (async)
putAsyncLazySingleton<T>(Future<T> Function() func, {String? named})putAsyncFactory<T>(Future<T> Function() func, {String? named})
- Resolve
- Sync:
get<T>({String? named}) - Async:
getAsync<T>({String? named})
- Sync:
Examples
Sync lazy singleton — same DateTime
final di = Dependency();
di.putLazySingleton<DateTime>(() => DateTime.now());
final v1 = di.get<DateTime>();
final v2 = di.get<DateTime>();
assert(identical(v1, v2));
Async lazy singleton (concurrency-safe) — same DateTime
final di = Dependency();
di.putAsyncLazySingleton<DateTime>(() async {
await Future.delayed(const Duration(milliseconds: 100));
return DateTime.now();
}, named: 'num');
final results = await Future.wait(
List.generate(10, (_) => di.getAsync<DateTime>(named: 'num')),
);
assert(results.toSet().length == 1); // created only once
Factories (sync / async) — different DateTime
final di = Dependency();
di.putFactory<DateTime>(() => DateTime.now());
di.putAsyncFactory<DateTime>(() async => DateTime.now());
final s1 = di.get<DateTime>();
final s2 = di.get<DateTime>();
assert(!identical(s1, s2)); // new instance each call
final n1 = await di.getAsync<DateTime>();
final n2 = await di.getAsync<DateTime>();
assert(!identical(n1, n2));
Named keys and defaults
final di = Dependency();
di.putSingleton<DateTime>(DateTime.now()); // key = 'DateTime'
di.putSingleton<DateTime>(DateTime.now(), named: 'two'); // key = 'two'
expect(di.get<DateTime>(), isA<DateTime>());
expect(di.get<DateTime>(named: 'two'), isA<DateTime>());
Set
final di = Dependency();
di.putSingleton<DateTime>(DateTime.now(), named: 'x');
di.set<DateTime>(DateTime.now(), named: 'x');
expect(di.get<DateTime>(named: 'x'), isA<DateTime>());
Important Rules
- If you register async (Async Lazy / Async Factory), you must use
getAsync<T>().- Calling
get<T>()will throw a clear error.
- Calling
- If you register sync, both
get<T>()andgetAsync<T>()are fine.
Errors
- Not found:
Exception('Key <name> not found') - Duplicate:
Exception('Key <name> already exists') - Wrong combination (e.g., async reg + get):
Exception('... Use getAsync() instead.')
Tests
Run all tests:
flutter test
License
MIT