Better API calls in Flutter [part 1]

Paweł S
5 min readMar 30, 2022

Creating your first app(s) with Flutter seems soo easy. The problems start with bigger apps. One of them might be handling API calls. We will look at how we can improve them in this article.

Starting point

This article is based on a simple app that loads a list of movies and tv shows from TheMovieDb. In the series of articles (yes, I am planning to do more), we will try to upgrade it to the next level.

Currently, our calls look like this

A starting point for our movie api

Not so bad, but there are some issues here:

  1. There is no error handling, we need to remember to catch it on other layers
  2. An API key is manually added to every url, the same goes for language. This is not a reusable solution. What would happen if we used JWT (JSON Web Tokens) and need to handle refreshing in every call? A total mess.
  • So, what do you recommend?
  • I’m glad you asked. As always, when we want to create something universal, we should create an abstraction. So, let’s do that!

Create abstract Api class

We need to create a new abstract class. In our case, it will be called Api. It will have a get method that returns an HTTP Get response for a given url. From the previous sentence, it seems, that we did not change much, and that is correct. Well.. almost. A response is not the same for each call so we need a universal type of response. One of the possibilities could be the use of dynamic type, but that is not the way to go. The better solution is to create another abstract class Response that can be successful or not. After applying those changes we will obtain:

Our new response classes, and updated Api class with the return type

Ok, that is a step forward, but there are a few more. When we would apply those changes to our initial call, we would obtain an error as the return type is not correct. Even after fixing it, one more error would be left in our response mapper, as the current class we returned wasn’t implementing a Response. We need to make some changes to fix the issue.

Upgrading return types

As of now, we returned directly the desired class, but it is not a Response type. The simple solution would be to implement Response or even better SuccessfulResponse. But we can go a step further and use Freezed library. That will generate us a fromJson constructor and other helpful methods. (Remember, this step is not required, and you can skip to the next part.)

Api response classes with JSON mappers

Ughh… This looks like a highly dens chunker… Aren’t all response classes with JSON mappers looking ugly?

Ok, we can do this, let’s break it down into smaller pieces, so we can understand what is going on.

Starting from the top we have ContentListResponse which implements SuccessfulResponse. Then we have the first constructor which tells freezed what fields the generated class should have. There we also specify that it should implement SuccessfulResponse. We can also notice that the first argument of the constructor has ContentResponseConverter() annotation. We will discuss it in a moment. Then there is a named constructor fromJson which is self-explanatory.

Next up is ContentResponse class. It has 3 constructors. The first one is for movies and the second one for tv shows, as our app displays both. They have almost the same fields in JSON. The difference can be seen in JsonKey for firstAirDate and title. That is the single reason we had to create two separate constructors. Fortunately, when operating on the ContentResponse object, we will have access to all the fields, as the names of the parameters in constructors are the same. The third one is obvious as before.

Now we can see where that ContentResponseConverter() annotation comes from. We had to create a JsonConverter so we can correctly choose which constructor should be used for our ContentResponse. To determine that, we are checking if the given JSON contains a key name, which is characteristic for the tv show response.

Api call with new response type

Having this out of the way, we can come back to our initial API call, perform necessary changes, and as the result, we get a simple call.

Updated call for movies with the new return type class in mapper

Hurray! We did it!

Wait… It looks almost the same, we still did not handle errors, and the API key is added manually… Don’t worry, the hard part is done. Now, the straightforward process is left. We will finish in no time.

Handling authorization and errors

Our movie app is authorized by adding api_key parameter into uri query. We can easily do that by obtaining the current parameters from uri which are represented as a map and adding api_key there. As for handling errors, we need to check the response’s status code and http.get() exceptions.

Our new base Api class with error handling and injecting api key to path parameters

After those changes, our call will look pretty similar to the previous one. The main difference is the removal of api_key from the query. We can do the same with language, but as always, it depends on our requirements.

Changes in repository

The last change in our code is left. Handling the response in repository.

Repository before and after changes for better api handling

Yes, I know, now it is a bit longer, but you can easily handle different types of responses or errors without raising an unhandled exception. In our case, we want to return an empty list when loading from API fails.

Every project is different, and a lot of them would need other HTTP methods other than GET. Now we know the idea, so adding others is pretty easy. The only thing we need to change is the http method.

What I have shown here is not the final form. In the next article, I will show a better way to handle the response and show customized error messages in the UI to the user.

This is my first article, I hope you enjoyed it. If you have any ideas what I can improve, let me know in the comments.

--

--

Paweł S

Passionate Android/Flutter developer that strives for perfection, which can never be achieved.