Clean Architecture on Flutter with GetIt
By: Amine Kilani
Tags: Dependency Inversion, Flutter, Flutter App Development, Clean Architecture, Dependency Injection

What is Clean Architecture ?

If you are already very familiar with Clean Architecture, you can skip this part.

There are much things to say about clean architecture that we can even write a book about it 😉, but I will try to be as concise as possible I promise !

Before that, I would like to face you to a problem, let’s take a look at the picture below. There are some school supplies tied to each other with some strings. For example, the scissors are tied to the pen but also to the scotch tape.

Messy architecture

Now, what If I want to replace the scissors with a knife ? I should undo the thread that connects the scissors to the pen, the other one tied to the ink, the one tied to the scotch tape and the one tied to the compass. These dependencies are really annoying when we want to replace an element with a new one.

Let’s take another case with the picture below and still the same problem, what if I want to replace the scissors with a knife. This is now an easy things to do since there is only one thread connected to the post-it. It is the same for all the other elements, we clean these messy dependencies, that is what Clean Architecture is for. (Yes, I use uppercase because it is awesome, sue me).

Clean architecture

Clean Architecture aims at reducing the dependencies of your business logic with the services you consume (API, Database, Framework, third party libraries) in order to maintain a stable application during its evolutions, its tests but also during changes or updates of external resources.

Clean architecture dependency rule

You may have encountered this diagram which is actually hard to understand at first site. We won’t dig into it here because there are many articles that already did it clearly.

I just want you to represent Clean Architecture as a way to divide your app into different layers with different dependency rules between them. That is what the black arrows are representing there, the dependency is going inwards. For example, the Entities shouldn’t know about the Use Cases but the Use Cases uses the Entities.

Clean Architecture can be used in any kind of app, from mobile apps to REST APIs (I use it for a Spring Boot app for example). Here, I will present a mobile app Clean Architecture as the title of this article says.

It consists in dividing the app into three layers UI (or Presentation), Data and Domain :

UI is the front, what is directly shown to the user. This is where you will find your MVP, MVC, MVVM or MVI patterns.

is the front, what is directly shown to the user. This is where you will find your MVP, MVC, MVVM or MVI patterns. Data is for data access (duh 🙄), this is where the local databases and the API’s calls logic will be found.

is for data access (duh 🙄), this is where the local databases and the API’s calls logic will be found. Domain is the heart, it’s where you find all your business rules in the form of Use Cases, Models, etc.

Just remember that Domain doesn’t have any dependency with UI or Data layers. Domain is a layer that must not have any references to the Framework you use since it is only pure business rule, it can be used in any other Framework. That is why Domain is the heart of your app.

UI and Data communicate with each other through Domain using Dependency Inversion principle, I will come back on that later.

These are the advantages of the Clean Architecture :

Separation of three concepts into layers: presentation, business rules, data access

Independence from frameworks

Testability

UI independence

Database independence

Better maintenance and readability

“ The goal of software architecture is to minimize the human resources required to build and maintain the required system. “

Uncle Bob

I will stop here for the Clean Architecture presentation before I lose you, I am sure that if you clicked on this article, you will be willing to learn more about this architecture. Let’s see how we can implement it on Flutter !

Dependency Injection in Flutter

The first ingredient for a good architecture is having a dependency injection framework. Of course, it is not mandatory and you can absolutely manage your dependencies manually, but when a good framework can facilitate your life why not use it ?

Some dependency injections can be done with Provider in Flutter. I had the opportunity to use it in some personal projects for injecting MobX stores in Widgets, but I didn’t like the fact that it can take some time to remove Provider from your project. I wanted something closer to Koin on Kotlin where dependencies are declared in separate files.

Then I found GetIt which is exactly what I was looking for ! The dependencies are declared in a separate file using Service Locators, we’ll see an example soon.

But why do we need dependency injections ? Because it will help us perform the Dependency Inversion principle (the D of SOLID). This principle allows an inversion of dependency so the Domain layer won’t have any direct reference to the Data layer but access it from abstractions (interfaces).

Implementing an architecture

Let’s take an example of a very simple app, one that only creates a user in Cloud Firestore based on two input fields : first name and last name. Very easy right ?

The first step is to create our layers : UI, Data and Domain. The layers are simple Flutter packages, I’ll also add a di package for dependency injection definitions.

Clean architecture package structure

Here is a very simple page with our two text fields and our button to create the user.

Now let’s create:

MobX UserStore with one method createUser() /UI

User model class with id, first name and last name/ Domain

Interactor to create the user /Domain

UserRepository interface /Domain

UserRepository implementation /Data

The Dependency Injection file with GetIt/DI

We will begin with the service_locator.dart file which defines all of our dependencies.

Using the service locator, we can define Singletons or Factories using registerSingleton or registerFactory. I advise you to use singletons for Stores and factories for Repository and Interactor, except if you want to retain a value through the instances.

As you can see, each time a UserRepository (interface in domain) is asked, a FirebaseUserRepository (implementation in data) instance is provided.

In the interactor below, we ask for a dependency by simply writing serviceLocator<UserRepository>(). As defined in the DI file, it will be indeed a FirebaseUserRepository that will be provided. This allows the Dependency Inversion so the domain layer doesn’t know anything about the data layer !

The repositories are very simple, an interface in domain and it’s implementation in data :

You see that a User model is used. This is a simple class defining the user model. This model class will be shared through all the layers so it is mandatory to have it in the domain layer, otherwise you would violate the dependency rule. I put all my models inside a “models” package in domain.

You can organize your packages inside the layers as you want, as long as you respect the layers distinction.

As you can guess, the store will be injected in the widget using the service locator and the interactor will be injected in the store.

This diagram summarizes the Clean Architecture implementation on Flutter. Of course, there are many ways to implement it and it can surely evolve.

Clean architecture in Flutter

I hope this article inspired you for your projects, I enjoyed writing it a lot since it’s my first story on Medium 😁

You can take a look at the full project on the Github repository here :

Praise Uncle Bob and have fun on your projects 👋