Basic and advanced networking in Dart and Flutter β€” the Tide way. Part 3: HTTP client and request interceptors with dio. Basic.

Feeling lost? Check out the introduction into this series.

Parts 3 and 4 of this series are dedicated to setting up an HTTP client used to load data from the backend.

This part aims to explore the dio object and show the basic setup. By the end of this part, the solution is ready for the first API request.

dio is a powerful HTTP client for Dart with the support of numerous critical and bonus features. For motivation, installation instructions, and basic implementation details refer to the docs.

In this part:

1. dio HTTP client
2. dio interceptors
3. first API request

For advanced topics check out Part 4. It talks about:

1. mocked API
2. proxy
3. SSL pinning

0. Prerequisites

When this series is released, the latest Flutter version is 3.0.

The latest versions of required dependencies in pubspec.yaml file are:

Further code examples are built on top of the code developed in Part 2, which can be found under the part-2 tag in the Flutter Advanced Networking GitHub repository.

1. dio HTTP client

dio is an object used to obtain data from the API. Each request can be configured individually, and dio also offers a BaseOptions class for common setup for all requests:

With this setup, each request will have an additional query parameter:
?parameter1=value1 and a header {"header1":"value1"}.

dio object exposes get, post, put, patch, delete, and other methods, which all are wrappers over a universal request method:

In this example:

the resulting GET request will be sent to https://example.com/api/get_example URL and have two query parameters: ?parameter1=value1&parameter2=value2, and two headers: {"header1":"value1", "header2":"value2"}. One header and one query parameter would come from the common dio setup, and another pair of header and query parameter would come from the setup in the getExample method.

Unlike get, other methods also accept dynamic data parameter to be sent in the request:

In this example:

the resulting POST request will be sent to https://example.com/api/post_example URL and have only query parameter and header from the common dio setup, and also a {"id": "example_id"} body.

Refer to the docs for more dio settings and methods. Moving on to the dio features we are using in Tide Flutter projects.

2. dio interceptors

A dio interceptor is an object with onRequest, onResponse, onError callbacks that are triggered when a respective event happens with dio object. Multiple interceptors can be attached to the same dio object.

Let’s say there is a requirement to send a timestamp with each request. One way would be to explicitly do so, as on line 11:

As you can imagine, this logic will quickly be duplicated all over the project. And it’s not that hard to forget about such a requirement here and there. Instead, this is a perfect case for an interceptor:

When attached to dio object, its onRequest method will append the ts query parameter to every request:

In fact, the Marvel Comic API imposes a more sophisticated requirement. Each request has to have three query parameters:

  • apikey β€” your public key;
  • ts β€” a timestamp (or other long string which can change on a request-by-request basis);
  • hash β€” an md5 digest of the ts parameter, your private key, and your public key, e.g. md5(ts+privateKey+publicKey).

Create an account on https://developer.marvel.com/ and find your pair of the public and private keys under the β€œMy Developer Account” tab.

Again, such a requirement can be easily satisfied with dio interceptor. To make the implementation a bit more interesting and to cover some real-life scenarios, the MarvelApiAuthInterceptor will accept the public key as a parameter, and the private key will be asynchronously obtained from an injected service:

After MarvelApiAuthInterceptor is attached to dio instance, all requests it makes to Marvel Comic API are authenticated:

Creating an array of interceptors is intentional here. There are several interceptors we typically attach to the dio instance in Tide projects.

3. First API request

By now we have a dio instance configured with the proper baseUrl and the interceptor, responsible for making authorized API requests. In addition, in previous parts, we created the MarvelComic model and the generic MarvelApiResponse class that allows parsing JSON responses from the Marvel Comic API.

It enables making the first API request!

The authenticated GET request to https://gateway.marvel.com:443/v1/public/comics URL returns a list of comics wrapped in a generic metadata response:

The MarvelComicsApi class accepts a configured dio instance as a constructor parameter on line 7. In its getComics method, it performs a get request to /comics path on line 12. The obtained json data of type Map<String, dynamic> is passed to MarvelApiResponse.fromJson constructor on line 15. The data field type of the generic MarvelApiResponse is MarvelPaginatedList, so a MarvelPaginatedList.fromJson constructor is called on line 16. The MarvelPaginatedList is also a generic class of type MarvelComic, so MarvelComic.fromJson is called on line 18. As a result, getComics method returns a parsed MarvelApiResponse object with a list of MarvelComic models.

Conclusion

We discovered what is dio and how to leverage its interceptors mechanism, and also made a first authorized request to the Marvel Comic API.

The final version of the code developed in this part is located under the part-3 tag in the Flutter Advanced Networking GitHub repository.

Read on Part 4: HTTP client and request interceptors with dio. Advanced.

--

--

Anna Leushchenko πŸ‘©β€πŸ’»πŸ’™πŸ“±πŸ‡ΊπŸ‡¦
Tide Engineering Team

Google Developer Expert in Dart and Flutter | Author, speaker at tech events, mentor, OSS contributor | Passionate mobile apps creator