GetIt — A great service locator for Flutter

Idan Ayalon
5 min readAug 26, 2022

This guide talks about get_it, a service locator for Flutter.

get_it is fantastic to get objects (super fast! O(1)) in many Flutter scenarios. It’s easy to use and doesn’t fuck with your UI tree as Providers do.

Let’s begin with the question, what’s, is a service locator anyway?

Dependency Injection or Service Locator are just implementations of the Dependency Inversion Principle.

Of course…In short, it’s a way to decouple an interface (in dart abstract class) from a concrete implementation, allowing you to access that instance from anywhere in the app.

When will we want to use it? The simple answer is as soon as possible before our project will grow and becomes complicated with tons of modules.

It’s essential to keep the business logic in the stores and separated from the UI, so we will find ourselves calling the store repeatedly in multiple places, at least we should. The locator get_it helps us do it with minimum effort. Another typical example is Repositories that live in the model layer. It will abstract data sources and separate them from the presentation layer.

Also, we will want to use them whenever we want to test APIs or the database. With get_it, we can quickly MOCK the repository in our tests. It’s widespread to have repository implementation of real REST API and another fake storage that returns Future.value(…) with simulated data.

Before we start

We will need to add this package to our pubspec.yaml

get_it: ^7.2.0

Cool, now run in the command line to get the package.

Flutter pub get

After that, the first thing we should do is to create a file (let’s call it — locator.dart), there we will define a global variable (yeah, I know what you are thinking, a global variable is terrible, no worries — you can call it whatever you want — sl, get, locator, di, sugar_mammy). To access the get_it instance across the app.

final sl = GetIt.I;

Under it, let’s add a method called — initLocator(), and call it as soon as possible when the app starts. This method (line 58) will define and register all the dependencies and helpful stuff (modules, repositories, services, stores, etc.…) we need. In line 68 the socket: sl() means that we are trying to call the socket dependency (line 73).

Also notice that in line 65 where we register that repository we register the repository abstract class and bind it to its implementation (ChatSettingsRepositoryImpl)

Our first example will be a repository I use in my app IconApp for the chat module.

Wow, it’s a big repo. Yup, we are dealing with a chat, we have stuff like get all the messages, listen to messages, likes, delete events coming from the socket (you can see they are all Stream type), also sending a new message (In IconApp we have five types of messages in the app), mute the conversation, and many more actions.

In our chat example, I use MobX as my state management, and my project structure is that I have a folder named ‘presentation’ in the root, all the UI-related stuff goes there. Inside it, I have another folder called ‘chat’ that represents the chat module. Inside it, is another folder (also folders like widgets, extensions, etc.) called ‘store’ there.

Most of the time, you’ll have just one store in the store in this folder, and it’s MobX, so it’s generated file that it creates (will cover MobX in greater detail in another article).

The ChatStore handles the chat’s business logic. For example, I want to send a message, so I have a method called sendMessage(String message, MessageType type). To send it, I need to access my repository abstract class in the data layer that has an implementation and needs to be bound to the abstract class.

In my repository implementation, I should call the REST (let’s say Retrofit/Dio) client or the database (Hive), or, of course, the socket service (Socket.io).

The _repository field is initialized in the store like so:

_repository = sl<ChatRepository>();

Now jumping to the UI, the SendButton widget that doesn’t know or care about the repository in the data model, knows only the ChatStore and can call sendMessage with its params:

Line 64 I call the chat store with the service locator and call sendTextMessage(), if you wonder why I don’t pass any param is that I have a ChatState object in my store that has all the information.

Now try to imagine this process without a locator, bah…I need a bucket. Also, when your boss will ask you to add some tests, you still want to kill him but much less. Just MOCK some repositories, and stores and finish with it, no problem. I hope you like this idea of locators. It will make your code readable, easy to use, and maintainable.

get_it offers tons of other cool stuff:

  1. check if a module is registered
  2. unregister a module
  3. use singletons
  4. use factory — will create a new object every time called
  5. can define scopes
  6. use asynchronous factories or singletons
  7. check if the dependencies are ready (in case of asynchronous)
  8. and testing — will cover in another article.

I hope you enjoyed it.

You can check IconApp or join my Flutter community on Facebook.

IconApp:

Android: https://play.google.com/store/apps/details?id=com.icon.iconapp

iPhone: https://apps.apple.com/us/app/iconapp/id1528197266

Facebook community:

https://www.facebook.com/groups/flutteril/

--

--