Reputation: 8417
Since there is no official library from the flutter team, I'm trying to implement the dependency injection in flutter manually using the singleton pattern, after a long search this is what I came up with:
class Injector{
Injector._internal();
static final _singleton = new Injector._internal();
factory Injector() => _singleton;
SomeClass get someClass => new SomeClass();
}
Now, Injector
is singleton that has one instance once instantiated and SomeClass
is the dependency I want to inject in my code. The above code works, but the problem is where should I instantiate the Injector
class and make it available every where in my code. Do you think Global Variable
is good in this situation or is there a better way? Thanks.
Upvotes: 3
Views: 1855
Reputation: 93
Clear example, fulfils all needs:
/// For use
abstract class Database {}
/// One of the implementations
class LocalDatabase extends Database {}
/// Lazy initialization factory
typedef Factory<T> = T Function();
/// Objects store
class InjectFactory<T> {
final Factory<T> factory;
T? _object;
InjectFactory(this.factory);
T get object => _object ?? (_object = factory());
}
/// Dependencies store
final store = HashMap<Type, InjectFactory>();
/// Methods
void register<T>(Factory<T> factory) => store[T] = InjectFactory<T>(factory);
T? get<T>() => store[T]?.object as T?;
/// Example
void main() {
test('Test', () {
register<Database>(() => LocalDatabase());
Database? db = get<Database>();
expect(db is Database, true);
expect(db is LocalDatabase, true);
expect(db.runtimeType, LocalDatabase);
});
}
Upvotes: 0
Reputation: 8417
This is my solution for this problem. First I created a dart file named injector.dart
with this code:
// the singleton is private to this package
final _injector = new _Injector();
// expose depedencies
final foo = _injector.foo;
final bar = _injector.bar;
class _Injector{
// create a singleton
_Injector._internal();
static final _singleton = new _Injector._internal();
factory _Injector() {
return _singleton;
}
// the dependecies
Foo get foo => new Foo();
Bar get bar => new Bar();
}
This is how the code work, first we create a singleton class _Injector
that creates needed dependencies and then exposes these dependencies with top-level variables. This way the dependencies are accessible anywhere the injector.dart
package is accessible.
What do you think guys? is this good or is there a better implementation? Thanks
Upvotes: 2
Reputation: 21441
To implement your own dependency injection I usually use a combination of
The getters which return classes should lazily construct them if they have dependencies. This allows you to override any parts of your graph by extending the Bindings class and setting it in the global bindings. For example, below I have three classes with the third depending on the first two.
class Foo {}
class Bar {}
class Fizz {
Fizz(this.foo, this.bar);
final Foo foo;
final Bar bar;
}
class Bindings {
/// Can be final since there are no dependencies
final Foo foo = new Foo();
final Bar bar = new Bar();
Fizz _fizz;
Fizz get fizz {
_fizz ??= new Fizz(foo, bar);
return _fizz;
}
}
Bindings get bindings => _bindings;
Bindings _bindings;
set bindings(Bindings value) {
_bindings = value;
}
Now suppose I want to override Foo for testing. I can extend the Bindings class and override the field/getter that returns Foo. and in my test setup, I set bindings with this new instance. Now when Fizz is created, the MockFoo instance is used instead of Foo
class MockFoo implements Foo {}
class BindingsOverride extends Bindings {
@override
final Foo foo = new MockFoo();
}
void main() {
bindings = new BindingsOverride();
}
Edit: In an earlier version I was using a static class. I don't think you need to refer to foo
and bar
through the bindings instance, you can just refer to the members directly.
Upvotes: 4