GetIt: Simplifying Dependency Injection with Service Locator Pattern in Dart and Flutter

Salman Bediya
6 min readMar 31, 2023

Dependency Injection (DI) and Service Locator (SL) are popular patterns used in software development. While they have similar goals, they have different approaches to achieving them. In this article, we will explore how to use the Service Locator pattern to implement Dependency Injection.

Dependency Injection is a pattern that allows objects to be created with their dependencies supplied from outside. This makes the objects more modular, easier to test and promotes loose coupling between components. Service Locator is another pattern that provides a centralized registry for services, allowing components to easily obtain the services they need. The Service Locator pattern can be used to implement Dependency Injection by acting as a container for all the services an application needs.

The GetIt package is a popular implementation of the Service Locator pattern in Dart, the programming language used for Flutter development. GetIt is a simple, lightweight, and easy-to-use package that allows developers to register and retrieve services from a central location.

To use GetIt, we first need to import the package and create an instance of the GetIt class. We can then register our services with the GetIt instance using the registerFactory or registerSingleton methods. The registerFactory method creates a new instance of the service every time it is requested, while the registerSingleton method creates a single instance that is reused each time it is requested.

import 'package:get_it/get_it.dart';

final getIt = GetIt.instance;

class MyService {
void doSomething() {
print('Something');
}
}

void main() {
getIt.registerSingleton<MyService>(MyService());

final myService = getIt<MyService>();
myService.doSomething();
}

In the example above, we register an instance of the MyService class as a singleton with the GetIt instance. We then retrieve the instance using the getIt function and call the doSomething method on it.

One of the advantages of using GetIt is that it allows us to easily manage dependencies between services. We can use named registrations to specify dependencies between services, and GetIt will automatically resolve them when the services are requested. We can also use the lazy parameter to defer the creation of a service until it is needed.

class MyService {
final SomeDependency someDependency;

MyService({required this.someDependency});

void doSomething() {
someDependency.doSomethingElse();
}
}

class SomeDependency {
void doSomethingElse() {
print('Something else');
}
}

void main() {
getIt.registerSingleton<SomeDependency>(SomeDependency());
getIt.registerFactory<MyService>(
() => MyService(someDependency: getIt<SomeDependency>()),
);

final myService = getIt<MyService>();
myService.doSomething();
}

In the example above, we register an instance of the SomeDependency class as a singleton and a factory for the MyService class that depends on SomeDependency. We retrieve an instance of MyService using the getIt function, and the dependency on SomeDependency is automatically resolved.

We can also use GetIt to retrieve instances of services in different contexts, such as within a Flutter widget or a background isolate. We can register services with different lifetimes, such as transient, singleton, and scoped, to control how they are created and destroyed.

GetIt provides a simple and flexible way to implement Dependency Injection with the Service Locator pattern in Dart and Flutter. By using GetIt, we can easily manage dependencies between services, promote loose coupling between components, and improve the testability and modularity of our code.

To summarize, here are some of the benefits of using GetIt for Dependency Injection:

  • Simple and lightweight
  • Easy to use and configure
  • Allows for easy management of dependencies between services
  • Promotes loose coupling
  • Provides flexibility in managing the lifetimes of services
  • Improves the testability and modularity of our code
  • Can be used in different contexts, such as within Flutter widgets or background isolates.

To further illustrate the advantages of using GetIt for Dependency Injection, let’s take a look at a comparison between using DI without GetIt and with GetIt.

Without GetIt, we would typically have to manually create and manage the dependencies between our services, as shown in the example below.

class MyService {
final SomeDependency someDependency;

MyService() : someDependency = SomeDependency();

void doSomething() {
someDependency.doSomethingElse();
}
}

class SomeDependency {
void doSomethingElse() {
print('Something else');
}
}

void main() {
final myService = MyService();
myService.doSomething();
}

In this example, we manually create an instance of SomeDependency and pass it to an instance of MyService. This can quickly become cumbersome as the number of services and dependencies grows.

With GetIt, we can simplify this process by registering our services and their dependencies with the GetIt instance, as shown in the earlier examples. This allows us to easily retrieve instances of services and their dependencies without managing them manually.

Consider the development of a Random Quote Generator app utilizing the GetIt dependency injection and the service locator pattern can be achieved in six steps.

The project consists of four essential files that support its implementation. These files are locator.dart, quote_controller.dart, quote_repo.dart, and quote_screen.dart.

  • The locator.dart file contains a GetIt instance that is created with the necessary class registrations.
  • The quote_controller.dart file helps obtain the Quote data from the Quote Repo.
  • The quote_repo.dart file fetches the data from the API.
  • The Quote is then bound from the Quote Controller to reflect on the UI through the quote_screen.dart file.

Step # 01: Create a global getIt locator instance

final locator = GetIt.instance;

Step # 02: Create a setup method and own service to register with the locator

void setupLocator() {
locator.registerLazySingleton<QuoteRepo>(() => QuoteRepo());
locator.registerLazySingleton<QuoteController>(() => QuoteController());
}

Step # 03: Call the setupLocator method and initialize the service in the main function

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

Step # 04: Get the QuoteRepo from the locator and call the getQuote method to fetch the quote from the API.

Future<Quote> getQuote() async {
return locator.get<QuoteRepo>().getQuote();
}

Step # 05: Finally, In a changeQuoteMethod, locate the service from GetIt by using the get method and call the method nextQuote method from QuoteController and assign it to displayQuote to display on the screen

Future<void> _changeQuote() async {
Quote quote = await locator.get<QuoteController>().getQuote();

setState(() {
displayQuote = quote;
});

}

This is how easily we can use it in our application. For the complete project source code, Click here.

You can also watch an explanatory video about GetIt with complete implementation here.

In addition to the benefits of using GetIt for Dependency Injection, there are also some potential downsides to be aware of. One potential issue is that using a Service Locator can make it harder to reason about the dependencies between components, as they are not explicitly defined in the code. This can make it more difficult to maintain and modify the codebase over time.

Another potential issue is that using a Service Locator can make it harder to write tests for our code, as the dependencies between services are not explicitly defined in the code. This can make it more difficult to isolate and test individual components of our code.

To mitigate these potential issues, it is important to carefully consider the use of a Service Locator and ensure that it is used appropriately and effectively in the context of our codebase.

In conclusion, GetIt is a powerful and flexible tool for implementing Dependency Injection with the Service Locator pattern in Dart and Flutter. It simplifies the process of managing dependencies between services, promotes loose coupling between components, and improves the testability and modularity of our code. Using GetIt effectively, we can build robust and maintainable applications that are easy to develop and test.

Hope you have enjoyed it!
Thanks for reading!

Keep supporting and don’t forget to applaud 👏 on the article if it helps you.

Other Famous Articles:

--

--

Salman Bediya

Developer | Open Source Contributor | Creator | Mentor | Public Speaker | Blogger | Entrepreneur | YouTuber. Let's Flutter with Dart