Dependency Injection is a technique widely used in programming and well suited to Android development. This technique makes it possible to have a loosely coupled codebase which creates room for.
- Code Reusability
- Ease of Refactoring
- Ease of Testing
In this article, I will walk you through steps on injecting dependencies in a flutter app. note that we will be using some packages to achieve this.
STEP 1
Installing required packages
- GeIt (Service Locator)
- Injectable (Code generator for GetIt)
- Build runner (Code generator)
After adding these packages our pubspec.yaml file should look like this except if you are using a different package version
GetIt This package uses the service locator design pattern to encapsulate the process involved in obtaining a service with a strong abstraction layer.
Think of a service locator as a central registry for services that on request returns a service required to perform a specific task
Injectable This package is a code generator for GetIt
Build Runner This package provides a concrete way of generating files using Dart code.
I will try to make this example as basic as possible so we can all get the concept behind Dependency Injection in flutter.
STEP 2
Implementing GetIt.
Define a global variable for our GetIt instance in a new dart file
Define a top level-function to configure our dependencies then annotate it with @Injectaleinit
Call the Generated func $initGetIt(), or your custom initializer name inside your configure func and pass in the getIt instance.
import 'package:get_it/get_it.dart';
import 'package:injectable/injectable.dart';
import 'injector.config.dart';
final GetIt getIt = GetIt.instance;
// You can customize your initializer name
@InjectableInit(
initializerName: r'$setUpGetIt', // the default is r'$initGetIt()'
)
Future<void> configureInjection() async => $setUpGetIt(getIt);
Call configureInjection() in the top-level main function before running the App.
void main() {
configureInjection();
runApp(MyApp());
}
STEP 3
Registering services.
We can describe services as classes responsible for carrying out specific tasks e.g:
- Handling user registration.
- Wrapping a third party service e.g firebase.
- Writing to local storage e.t.c
We won't be registering services directly, we will work with an abstract class in order to have a level of abstraction
Implementing the abstract service class
abstract class AccountService {
Future<BaseResponse> login(String email,String password);
Future<BaseResponse> register(String email,String password,String phoneNumber);
Future<BaseResponse> lookUpUser(String phoneNumber);
}
Note: Since we are making use of an abstract class we will have to bind the abstract class to its implementation. to do this we will make use of the "injectable(as: )" annotation.
@injectable(as: AccountService)
)
class AccountServiceImpl implements AccountService{
@override
Future<BaseResponse> login(String email, String password)async{
// code block to perform operation
}
@override
Future<BaseResponse> register(String email, STring password) async {
// code block to perform operation
}
}
Usecase
@injectable
class AccountUseCase {
final AccountService _accountService;
AccountUseCase(this._accountService);
Future<BaseResponse> login(
String email,
String password) async {
return await _accountService.login(
email,password);
}
}
To register the service above we will have to run the below command.
flutter pub run build_runner build
The above command triggers a code generator that automatically registers our service with the help of injectable which is also a code generator for GetIt
Accessing Services
To access the registered service all you need to do is
getIt.get<AccountUseCase>();
And this can be used from anywhere in our codebase.
I hope you enjoyed reading this flutter tutorial, please feel free to leave any comments or feedback on this post!