How I Do Dependency Injection In Dart
2020-11-14Dependency injection is a common design patteren (you can check What Dependency Injection Is? And Why? for more about it.). For my dart projects, I use two packages, get_it
and injectable
. They serve different purposes, and together they provide smooth experiences for dependency injection.
Short Intro Of get_it
get_it
is a simple service locator. Object instances and object factories can be register and access in one single place.
How to register an instance
final getIt = GetIt.instance;
void setup() {
getIt.registerSingleton<AppModel>(AppModel());
// Alternatively you could write it if you don't like global variables
GetIt.I.registerSingleton<AppModel>(AppModel());
}
How to reference the regstered instance
MaterialButton(
child: Text("Update"),
onPressed: getIt<AppModel>().update // given that your AppModel has a method update
),
Short Intro Of injectable
Manually managing dependencies are tedious since it results many boilerplates.
This hurts especially when dependency graph is complex.
Source: http://kospiotr.github.io/wiki/spring-core-presentation/
Managing such dependency by hand? I prefer not. 😅
injectable
generates get_it
services register code with dependencies for you so that you don't have to worry about that anymore.
What you write
@injectable
class ServiceA {}
@injectable
class ServiceB {
ServiceB(ServiceA serviceA);
}
What injectable
generates
final getIt = GetIt.instance;
void $initGetIt(GetIt getIt,{String environment,EnvironmentFilter environmentFilter}) {
final gh = GetItHelper(getIt, environment);
gh.factory<ServiceA>(() => ServiceA());
gh.factory<ServiceB>(ServiceA(getIt<ServiceA>()));
}
How To Use
1. Include dependencies in pubspec.yaml
First, add these two packages and their dependencies in proper sections of pubspec.yaml
, and then run the command flutter pub get
to fetch them to development machine.
dependencies:
# add injectable to your dependencies
injectable:
# add get_it
get_It:
dev_dependencies:
# add the generator to your dev_dependencies
injectable_generator:
# of course build_runner is needed to run the generator
build_runner:
2. Configure various dependencies with annotations
Annotate classes you need to inject dependencies or use later
@Singleton()
class ServiceA {}
@Injectable()
class ServiceB {
ServiceB(ServiceA serviceA);
}
Class annotated with @Singleton()
will result a singleton in get_it
, while @Injectable()
will register a factory that will return a new instance everytime when called via get_it
.
Personally I don't use shortcuts
@injectable
and@singleton
as official example onpub.dev
page since they cannot pass parameters to customize the annotation.
3. Define a top level function to initialize dependencies
Create a new dart file and add the following code to tell injectable
where to place generated code.
final getIt = GetIt.instance;
@InjectableInit(
initializerName: r'$initGetIt', // default
preferRelativeImports: true, // default
asExtension: false, // default
)
void configureDependencies() => $initGetIt(getIt);
4. Generate code
Run the command flutter pub build_runner build
and let injectable
to do its job.
5. Summon any services anywhere at your will
In anywhere of your code, you can simplly use get_it
to access to all instances and factories you have annotated before.
As normal variables
var serviceA = GetIt.I<ServiceA>();
var serviceB = GetIt.I<ServiceB>();
As a change provider
ChangeNotifierProvider.value(
value: getIt<AppConfigViewModel>(),
child: ...
)
As a view model
MaterialButton(
child: Text("Update"),
onPressed: getIt<AppModel>().update // given that your AppModel has a method update
),
Closing
Here, I have shared one way to manage dependencies in dart projects. While my dart projects are much more organized following this path, I hope this will also help you and your dart projects.
If you like this article, remember to show your support by buy me a book.