flutter dependencies injection
dependencies injection in flutter

Making an object’s dependencies, which are typically referred to as services, accessible through another object is a technique called dependency injection.

A quick search of the Dependency Injection in Flutter Community will give you a list of articles. All are very informative and offer libraries and service locators to implement Dependency Injection in flutter . This post will offer an alternative to implementing code and or annotations to supplying  DI in flutter app .

What Is Dependency Injection ?

Dependency Injection (DI) is a technique used to increase code reuse by reducing tight coupling between classes. The dependent classes and objects are supplied to or injected into your class rather than being created or instantiated by your class, maintaining a sort of external library to create and manage object creation in your project. These classes can now clearly concentrate on their actual application logic because you have decoupled object creation from your actual view and model. Furthermore, some of the classes that we construct are singletons as a result of you as well.


Why does Flutter use Dependency Injection?

so as to increase code reuse by reducing coupling between classes.

to design a class that is not dependent on anything else.

Additionally, it lessens the possibility that you will need to modify a class simply because one of its dependencies changed.
A class can become independent of its own dependencies by using dependency injection. Because every class can call any dependencies it requires, you can divide your application’s various components into more easily maintainable sections. By doing this, a loosely coupled application is produced, making it easier and quicker to fix bugs and add new features.


Advantages of dependency injection

Applications built with Flutter can benefit greatly from dependency injection. Some of the main advantages are as follows:

  1. The injection is offered everywhere.
  2. This method automatically tracks instances.
  3. Registration against interfaces is feasible.
  4. The streamlined setup code can be used.

Disadvantages of Dependency injection

While dependency injection has a number of advantages, there are also some potential disadvantages to take into account:

1): The top priority for dependency injection techniques is not disposing .

2) :It complies with lazy coding standards which makes development challenging  .

3): The fact that it is a global object adds to the complexity .

Overall, in spite of the fact that dependency injection can have many benefits. it may not always be the best choice but Before deciding whether dependency injection is the best course of action for your specific use case. It is necessary to consider its advantages and disadvantages.


Now implement the other side….. 

By developing a sample app, we will show you how to using  GetIt and Injectable to implement dependency injection in your  Flutter application . We will discuss the following :

Why use GetIt and Injectable?

This is a simple Service Locator for Dart and Flutter projects that includes some extra goodies that were heavily influenced by Splat. It can be used to access objects, such as those in your UI, instead of InheritedWidget or Provider.

Common Dependency Injection Techniques include:

i): The fundamental strategy we use to inject dependencies using constructors.

ii):  A class called Inherited Injection should extend from inherited widgets. (But passing objects without context becomes complex in this technique)

Now , implement Dependency Injection Through GetX

Installation

Step 1): Add this following dependencies project pubspec.yaml file:

get_it: ^any
get: ^any

Step 2): Then import it. after installing the package using the terminal command line:

flutter pub get

Step 3): import the file

import 'package:get_it/get_it.dart';

Step 4):  Create a locator.dart file :

Create a get_it instance

GetIt locator = GetIt();

Add this

import 'package:get_it/get_it.dart';

void setupLocator()
{
 /// doo here your logic
}

Step 5) : Use this given code and  set in the your project directory main.dart file to import locator and you can continue :

void main()
{
setupLocator();
runApp(MyApp());
}

 When creating locators there are two ways to register class types using get_it . thorough explanation of this is provided below:

i) Factory: Each time your request for an instance of this class type is processed by the service provider, you will receive a fresh instance. This technique works well for registering view models that must execute the same logic upon startup or for models that must be fresh upon view opening.

ii) Singleton: You have two options for registering this method. You can either provide an implementation at the time of registration or a lama that will be called the first time your instance is requested. When this method is called, the locator will always return the single instance of your registered type that it has stored.

To register your classes, add a locator.dart file or use the code below:

void setupLocator()
{
// Add class here
locator.registerFactory(()=>AppInfo());
}

Step 7) : Next….. use the given code to add injectors or locators to the application screens:

var appInfo = locator<AppInfo>();

Usage

We have add dependency injection using getx

GetX Dependency Injection Techniques Have These Benefits:

  1. The streamlined setup code can be used.
  2. Registration against interfaces is feasible.
  3. Injection is accessible everywhere.
  4. This method automatically tracks instances.

To inject the object of the class use Get.put

When this method is used the dependency will load immediately and can be used directly by implementing below code 

 HomeController homeController = Get.put(HomeController());

Use “Get.find” to locate the controller in our application lifecycle following injection.

When multiple shared instances need to be updated separately, we can also use the Get.find tag property. Use the following code to accomplish this:

 HomeController homeNextController = Get.put(HomeController());

If the route using Get.put is removed from the navigation stack, the dependencies will also be deleted. Therefore, we must stop this from happening and maintain the dependencies in internal memory for the duration of the app session using permanent properties, as demonstrated below:

Get.put(Controller(), permanent: true);

The main three types of dependency injection are:

  • 1 Constructor Injection
  • 2 Property Injection
  • 3 Method Injection
1) Constructor Injection

a class’s function Object() is used to send dependencies to it ,  For an example  the class’s Object() function would accept the service as a parameter if the class required an instance of the authentication service.

class AuthenticationService {
  // Authentication service implementation
}
class UserRepository {
  AuthenticationService authService;
  UserRepository(this.authService);
  /// UserRepository implementation
}
2) Property Injection

Dependencies are set as properties of a class in the property injection method. For an example you would create a property for the service and set it when creating an object of the class .  if your class required an instance of an authentication service.

class AuthenticationService {
  ///  Authentication service implementation
}

class UserRepository {
  AuthenticationService authService;
  setAuthenticationService(AuthenticationService authService) {
    this.authService = authService;
  }
  /// UserRepository implementation
}
3) Service Locator

An object called a service locator keeps track of dependencies and gives users access to them. In this method  you would call up instances of dependencies as needed using a service locator.

class AuthenticationService {
  /// Authentication service implementation
}

class UserRepository {
  /// UserRepository implementation
}

class ServiceLocator {
  AuthenticationService getAuthenticationService() {
    return AuthenticationService();
  }

  UserRepository getUserRepository() {
    UserRepository repository = UserRepository();
    repository.setAuthenticationService(getAuthenticationService());
    return repository;
  }
}

Inside the global getIt object, we specified Auth as the actual implementation of AuthRepository.

With getIt, we can now access AuthRepository from anywhere in our UI code:

Future<void> _onClickSignIn() async {
  await getIt<AuthRepository>().signIn(email, password);
}
/// here add gorilllas app to appstate method


let’s start

Step 1): Create a UserData model :

class UserData {
  String name;
  String email;

  UserData({required this.name, required this.email});
}

Step 2): Create an interface for the UserRepository

abstract class UserRepository {
  Future<UserData> getUserData();
  Future<void> setUserData(UserData userData);
}

Step 3): The UserRepo known as UserRepository was injected . That uses  GetStorage in our application to store and retrieves the  user data.

import 'package:get_storage/get_storage.dart';
import 'user_data.dart';

class UserRepositoryImpl implements UserRepository {
  final storage = GetStorage();

  @override
  Future<UserData> getUserData() async {
    final data = storage.read('userData');
    if (data != null) {
      return UserData.fromJson(data);
    } else {
      return UserData(name: '', email: '');
    }
  }

  @override
  Future<void> setUserData(UserData userData) async {
    await storage.write('userData', userData.toJson());
  }
}

Step 4 ): Create a class that uses UserRepository to manage user data. Calling UserDataController now

import 'package:get/get.dart';
import 'package:get_it/get_it.dart';
import 'user_data.dart';
import 'user_repository.dart';

class UserDataController extends GetxController {
  final UserRepository _userRepository = GetIt.I<UserRepository>();
  Rx<UserData> userData = UserData(name: '', email: '').obs;

  @override
  void onInit() {
    super.onInit();
    getUserData();
  }

  Future<void> getUserData() async {
    userData.value = await _userRepository.getUserData();
  }

  Future<void> setUserData(UserData userData) async {
    await _userRepository.setUserData(userData);
    this.userData.value = userData;
  }
}

In this class we use GetIt inject the UserRepository instance and we use Rx<UserData> to make the user data observable .

Step 5): Establish dependencies Put it in the main Dart file:

import 'package:get_it/get_it.dart';
import 'user_repository.dart';
import 'user_repository_impl.dart';

void main() {
  GetIt.instance.registerLazySingleton<UserRepository>(() => UserRepositoryImpl());
  runApp(MyApp());
}

For this example code click here 👍

Conclusion

Flutter’s use of DI makes it simple to manage dependencies, testability, and provides a more scalable architecture. Dependencies can be easily swapped out during testing, mocked, or even have different implementations depending on the environment thanks to DI.

Overall, implementing DI in Flutter can result in cleaner, more maintainable code, aid in code separation and organization, and make it simpler to collaborate on bigger projects with multiple team members.

Is this a small example of dependency injection in flutter , you can modify this code by your needs.

Thanks for reading this article….. ❤️‍

Have a nice day…..