This package is on pub dev here
It's on Github here
class IocContainer {
final Map<Type, Object Function(IocContainer container)> _map;
IocContainer(this._map);
T get<T>() => _map[T]!(this) as T;
}
class IocContainerBuilder {
final Map<Type, Object Function(IocContainer container)> _map = {};
IocContainerBuilder add<T>(T Function(IocContainer container) get) {
_map.putIfAbsent(T, () => get as Object Function(IocContainer container));
return this;
}
IocContainer toContainer() => IocContainer(
Map<Type, Object Function(IocContainer container)>.unmodifiable(_map));
}
extension Extensions on IocContainerBuilder {
IocContainerBuilder addSingleton<T>(T service) => add((i) => service);
}
This is a Dart implementation of an Ioc container. It's so simple that I feel like I've missed something. It handles singletons and transient dependencies but you would need to handle any other kinds of life spans yourself.
Here are some tests:
import 'package:ioc_container/ioc_container.dart';
import 'package:test/test.dart';
class A {
final String name;
A(this.name);
}
class B {
final A a;
B(this.a);
}
class C {
final B b;
C(this.b);
}
class D {
final B b;
final C c;
D(this.b, this.c);
}
class AFactory {
final Map<String, A> _as = {};
A get(String name) {
final a = A(name);
_as.putIfAbsent(name, () => a);
return a;
}
}
class SomeService {
late final A a;
late final A aa;
SomeService(AFactory factory) {
a = factory.get('a');
aa = factory.get('aa');
}
}
void main() {
test('Basic singleton', () {
final a = A('a');
final builder = IocContainerBuilder();
builder.add((i) => a);
builder.add((i) => B(a));
final container = builder.toContainer();
expect(container.get<B>().a, a);
});
test('Basic Singleton 2', () {
final a = A('a');
final builder = IocContainerBuilder();
builder.addSingleton(a);
builder.add((i) => B(a));
final container = builder.toContainer();
expect(container.get<B>().a, a);
});
test('Method chaining', () {
final a = A('a');
final builder = IocContainerBuilder();
builder
.addSingleton(a)
.add((i) => B(i.get<A>()))
.add((i) => C(i.get<B>()))
.add((i) => D(i.get<B>(), i.get<C>()));
final container = builder.toContainer();
var d = container.get<D>();
expect(d.c.b.a, a);
expect(d.c.b.a.name, 'a');
});
test('Named Key Factory', () {
final builder = IocContainerBuilder();
builder
.addSingleton(AFactory())
.add((container) => SomeService(container.get<AFactory>()));
final container = builder.toContainer();
var someService = container.get<SomeService>();
expect(someService.a.name, 'a');
expect(someService.aa.name, 'aa');
});
}
Is there anything obviously missing here? Any bugs or issues?