Getting started with Service Locator Pattern in Flutter.

demola malomo
6 min readAug 11, 2021

--

Vishnu Mohanan on Unsplash

The service locator pattern is a design pattern in software engineering used to encapsulate the processes involved in obtaining a service with a strong abstraction layer. The basic idea of a service locator pattern is to have a central registry (also knows as a Service Locator) that holds all the services that an application might need. It offers advantages like adding codes to the application at run-time without re-compiling, separating functionality in codebases, and removing code redundancy (Don’t Repeat Yourself).

In this tutorial, we will learn how to use the service locator pattern in flutter to consume multiple API services. For the backend services, we will be using the JSONPlaceholder. JSONPlaceholder is a free online API service.

You can code along by cloning this repository (main branch)
here. If you prefer to view the complete code, checkout to the dev branch of this same repository.

In this tutorial, we will focus on implementations only. The project UI has already been set up.

Prerequisite

We will be needing the following:

Folder Structure

project folder structure

Let’s go over some of the key directories and files from above:

  • screens: a folder to store the screens of our application.
  • utils: a folder to store reusable classes.
  • widgets: a folder to store building blocks of our application.
  • main.dart: the entry point of our application.

Running the Project

We need to navigate to the project location, open the terminal, and install project dependency.

install project dependency

Then run the project using the command below:

running the project

The command above will run the application on the selected device.

Demo Application

The application consists of screens to display a list of users and todos.

Let’s code

With the project up and running, we can start creating files and installing dependencies needed to create a service locator pattern in flutter.

Step 1
We need to add the flutter’s http and get_it packages to our codebase. The http package is a future-based library for making HTTP requests, while get_it is a package for creating service locators in flutter and dart projects.

Open the pubspec.yaml file in the root directory, navigate to the dependencies section, add the snippet below and save.

dependencies
adding dependencies to pubspec.yaml

PS: Visual Studio Code installs the dependencies for us automatically when we save the file. We might need to stop our project and run flutter pub get to install the dependencies for other editors.

Step 2
We need to create a models folder in the lib directory and, in the folder, create a user_model.dart and todo_model.dart file. We will use the classes in these files to describe selected properties JSONPlaceholder users and JSONPlaceholder todos endpoint returns.

user_model.dart
todo_model.dart

We also included a factory method to parse the data we obtain from the API.

Factory in dart lets us return an existing instance of a class instead of creating a new one. This process helps us improve application performance.

Step 3
We also need to create an api_response.dart inside the models folder to handle API responses and errors.

api_response.dart

The APIResponse class takes a Generic of type T to type the data the API returns. The class also has isError and errorMessage property to handle any error the application might encounter.

Step 4
We need to create a services folder in the lib directory and, in the folder, create a user_service.dart file.

updated folder structure

We need the user_service.dart file to handle API calls to our user’s endpoint.

user_service.dart

The above code performs these tasks:

  • Import required dependencies.
  • Creates a UserService class with a method to get a list of users.
  • Returns a type of APIResponse with parsed data (JSON format) when the request is successful and with errors if there are any.

Step 5
We also need to create a todo_service.dart in the service directory file to handle API calls to our todos endpoint.

todo_service.dart

Finally, Service Locator and Consuming the Services

With the services for handling API calls sorted out, we need to set up a service locator to manage all the services (UserService and TodoService) in our application.

Setting up Service Locator
To set up a service locator, we need to modify main.dart file in the lib directory

updated main.dart

We imported the get_it package, UserService, and TodoService class. Then created a serviceLocator function, and registered our services using one of the inbuilt GetIt method( registerLazySingleton). registerLazySingleton creates an instance of the required service only on the first call of the service and, in turn, reduces app start-up time.

GetIt has multiple methods that cater to different use cases. We can read more about the methods here.

Finally, we need to include the serviceLocator function on the app start-up function.

Consuming the services

Get List of Users
First, we need to modify user_screen.dart in the screen directory as shown below:

updated user_screen.dart

We modified our code by:

  • Importing required dependencies.
  • Creating an instance of our UserService using the GetIt service locator. Declaring variables to initialize the API response, loading state, and length of the response.
  • Creating a _fetchUser method to make API call and manage states. Then, we called the function inside the initState . initState initializes the function when the widget is inserted in the widget tree.
  • Wrapping the ListView.builder widget with a Builder widget. The Builder helps us write a better conditional statement and avoid nested ternary operators. We also made provision for errors, loading state, and updated the UserCard with the current user object.

PS: UserCard will show an error about a user parameter not defined. We will fix this by updating the user_card.dart as shown below

updated user_card.dart

We initialize the UserCard class with a required constructor and then modify the name, email, and username accordingly.

With this, our outcome should be like this.

List of users

Get List of Todos
We will also modify todo_screen.dart in the screen directory as shown below:

updated tod_screen.dart

The bang(!) and question mark(?) in front of variables tells the compiler to relax the non-null constraint error (Meaning the parameter can be null)

Following the same pattern we did for users, we modified by:

  • Importing required dependencies.
  • Creating an instance of our TodoService using the GetIt service locator. Declaring variables to initialize the API response, loading state, and length of the response.
  • Creating a _fetchTodo method to make API call and manage states. Then, we called the function inside the initState . initState initializes the function when the widget is inserted in the widget tree.
  • Wrapping the ListView.builder widget with aBuilder widget. The Builder helps us write a better conditional statement and avoid nested ternary operators. We also made provision for errors, loading state, and updated the TodoCard with the current todo object.

PS: TodoCard will show an error about a todo parameter not defined. We will fix this by updating the todo_card.dart as shown below:

updated todo_card.dart

We also wrapped the nested Column with a Flexible widget to resolve RenderFlex Overflowed errors.

RenderFlex Overflow error

With the error fixed, our outcome should be like this:

List of Todos

Conclusion

This post discussed the process of setting up a service locator pattern in flutter applications. We also learned how to separate the user interface from the logic and set up services without multiple instantiations of classes.

You may find these resources helpful:

--

--

demola malomo
demola malomo

Written by demola malomo

A Tech Enthusiast | I constantly learn new software concept and blog about it.

No responses yet