Robust API structure for Flutter

Sumeet Rukeja
Flutter Community
Published in
4 min readDec 15, 2019

Hello, Flutter birds! When your app starts to expand, do you ever struggle to keep control on the network(API) architecture of your project?

I often used to get entangled into problems before I came up with a sturdy solution. I’m writing this article to share the solution with the beautiful community.

Why do we need it?

Before we get into it, let’s talk about FutureBuilder. It’s a handy widget to deal with API calls. It triggers an event after API returns the result. But in some cases, it can be overwhelming to work with it. If this pattern is not generalized, it increases code redundancy when it comes to error handling.

I, being an Android developer, am a huge fan of a concept called “LiveData”. One of its features is to trigger an event when any data is set into it. I’ve tried replicating this feature in Flutter which is the sole of this structure. I call it Observable.

observable.dart

Control flow

The main aim of this structure is to have a generalized and modular pattern. The following image shows the blueprint of it.

Network layer

I’ve used Dio library to work with APIs. It has features to set time outs, handle API cancellation, get upload progress and much more.

api_connector.dart

This is basically an API helper class which configures Dio and has all the basic tools to execute a network request. Dio’s CancelToken, which is used to cancel API requests, is initialized here. If you need to avoid direct communication with this class and want to have an abstraction layer on it, you can make a util class for it as I’ve done here.

api_util.dart

This class is used to declare all the APIs that will be used in the app. As seen in the above gist, I’ve created a get request using JSONPlaceholder.

Base classes

To have a generalized structure, one must have base classes in architecture. We’ll use a base class for each module in the structure i.e. Screen, Model, and Repository.

Base Repository

base_repo.dart

The repository is used to communicate with data sources. A single instance for ApiUtil is created here. To have a singleton pattern, I’ve used GetIt library. All the API exceptions are first caught in BaseRepo and then they are redirected to respective screen.

Base Model

Model is something that will create a communication bridge between screen and repository.

base_model.dart

It has an observable which will observe the API errors and will deliver events to screen for further processing.

Base Screen

Base Screen is similar to Base Activity in Android. It acts as a parent for all the screens of the app. The main aim of this class in our case is to process API errors and to manage API cancellations when the screen gets unmounted.

Connecting the dots

Now, as we are clear with all the components of the structure, let’s call the API from our screen.

home.dart

This is an example of a screen. We have created an instance of a model class for this screen which provides us with a method to call the API and a data observer. We are observing API data in the initState() method which can further be handled based on requirements. API is being called on a button press.

model.dart

This is the model class whose instance was created on the screen. It has a repository object to communicate with the repository of the screen which further will handle our API call.

repo.dart

This is pretty much the final part of our puzzle. The model calls the method getData() with ApiError observer to wire up the entire circuit. (ApiError can be any custom object which consists of data needed when API winds up in an error)

We can also pass request code for an API call, if needed. This circles back when handling errors and helps us to identify the API which threw error to take actions accordingly.

Let’s suppose that we need to show a Snackbar in case of an error for all the APIs of the app. We just need to add code in one place. Isn’t it a real time-saver?

This architecture is inspired by Android’s MVVM pattern and it fits for Flutter too. Using this, the app can easily be scaled to its massive form keeping the code cleaner, readable and spaghetti-free!

And, not only for API calls, this structure can be used as the basic building block for your app which divides your app into neater modules to work with.

Here’s my git repository consisting of a demo project for you to explore. I hope you enjoyed reading the article and grabbed something from it. Also, if you’ve any suggestions or ideas to make this structure more stable, please feel free to drop a comment and we can discuss it.

That’s all for now. See you later, folks!

Happy Flutter-ing :)

--

--

Sumeet Rukeja
Flutter Community

Technology enthusiast, a F.R.I.E.N.D.S fan & a gamer