Compile time dependency injection using factories, implmented using source_gen package, so that it works in every platform. The advantages of this approach are are performance (no reflection needed) and full debuggability, as the generated sources are easy to read and well formatted.
Install
To use this package, add auto_factory
and build_runner
in the dev_dependencies section
and the auto_factory_annotation
package in the dependencies section of your pubspec.yaml file.
Then, run pub run build_runner build
to generate the part files.
Table of contents
Basic usage
To make a class part of the dependecy tree annotate the class with the @Injectable() annotation. The library satisfies the dependency by creating a new instance and injecting to the default constructor the required dependencies.
Note that the injection will work only on default constructor with positional arguments. See example directory.
import 'package:auto_factory_annotation/auto_factory_annotation.dart';
part 'myfile.g.dart';
@Injectable()
class Component {
final OtherDependency dep;
Component(OtherDependency this.dep);
void doSomething() {
//...
}
}
/// in another file:
final component = await ComponentFactory().create();
component.doSomething();
Singletons
Using the @Singleton() annotation has the same effect as @Component() except that the instance is only created the first time. Then the same instance is returned every time
import 'package:auto_factory_annotation/auto_factory_annotation.dart';
part 'myfile.g.dart';
@Singleton()
class Component {
final OtherDependency dep;
Component(OtherDependency this.dep);
void doSomething() {
//...
}
}
/// in another file:
final component = await ComponentFactory().create();
component.doSomething();
Providers
Sometimes you don't have access to imported objects, so you can't annotate them to make it part of your dependency graph. In this case you can create a class annotated with @Provider() that extends @ProviderBase and implement the provide() method
import 'package:auto_factory_annotation/auto_factory_annotation.dart';
part 'myfile.g.dart';
@Provider()
class IpProvider extends ProviderBase<String> {
@override
Future<String> provide() async {
return '127.0.0.1';
}
}
And then mark the injected field with the @ProvidedBy() annotation like this:
import 'package:auto_factory_annotation/auto_factory_annotation.dart';
part 'myfile.g.dart';
@Component()
class Component {
final String ip;
Component(@ProvidedBy(IpProvider) String this.ip);
void doSomething() {
//...
}
}
/// in another file:
final component = await ComponentFactory().create();
component.doSomething();
Providers can have their dependencies resolved too with both component and other providers:
import 'package:auto_factory_annotation/auto_factory_annotation.dart';
part 'myfile.g.dart';
@Provider()
class IpProvider extends ProviderBase<String> {
final Config config;
IpProvider(this.config);
@override
Future<String> provide() async {
return this.config ? '127.0.0.1' : '11.22.33.44';
}
}
Example
The auto_factory/example directory contains a full example