Basic and advanced networking in Dart and Flutter β€” the Tide way. Part 5: REST API requests with retrofit. Basic.

Feeling lost? Check out the introduction into this series.

Parts 5 and 6 of this series are dedicated to an efficient implementation of REST API requests.

This part aims to show the basic usage of the retrofit package. By the end of this part, the first API call to the Marvel Comic API from Part 3 is reimplemented with code generation.

retrofit is a code generating package that creates calls to a passed-in dio object based on annotations. For motivation, installation instructions, and basic implementation details refer to the docs.

In this part:

1. simple API request
2. API request attributes
3. generic API responses

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

1. headers for individual requests
2. headers for most / all requests
3. headers for selected requests
4. pre/post request actions

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 4, which can be found under the part-4 tag in the Flutter Advanced Networking GitHub repository.

1. Simple API request

Take a look at the example:

The getExample method consists of four lines of mostly boilerplate code, while the meaningful information is only the /get_example path on line 11 and the ExampleResponse return type on line 13. Wouldn’t it be cool to only specify a request path, parameters, and expected response type, and leave everything else to the code generation? That is what retrofit is here for.

Here is the new ExampleApi implementation:

The @RestApi attribute on line 9 annotates the entire ExampleApi abstract class on line 10. Factory constructor accepts a dio instance, and returns an _ExampleApi constructor, which is a generated inheritor class. The getExample method on line 14 declares its return type of ExampleResponse, and is annotated with @GET attribute on line 13, accepting the /get_example request path. The part annotation on the line 7 informs about the file name for the generated code.

The generated .g.dart file contains:

The generated _ExampleApi class implements the abstract ExampleApi class declared before. In essence, the code is very similar to what was written in the manual implementation of the ExampleApi. The dio object injected in the constructor on line 6 is requested for Map<String, dynamic> data on line 18 via GET request on line 20 to the /get_example path on line 21. The response is parsed to ExampleResponse via ExampleResponse.fromJson() method on line 23.

2. API request attributes

Here is another example with more retrofit attributes:

The @RestApi attribute on line 5 now accepts a baseUrl parameter. The postExample method on line 10 is of type @POST, and its path contains an id, which passed as a parameter to postExample method with @Path attribute on line 11. The postExample method sends data of type Map<String, dynamic>, which is annotated with @Body attribute on line 12. The return type of postExample is specified in method declaration on line 10. The putExample method is quite similar, but it performs a @PUT request. It also accepts a @Path parameter id on line 17, but additionally has a @Query parameter named apiKey on line 18. It’s @Body on line 19 is of a custom ExampleBody type. Finally, the deleteExample method declared on line 23 only has a @Path parameter id and performs the @DELETE request.

The generated .g.dart file contains:

Base URL, request types, paths, and query parameters were copied from the abstract ExampleApi class declaration to the generated implementation.

retrofit allows specifying only the essential information about each request and generates all the boilerplate code. Refer to retrofit documentation to explore more features.

3. Generic API responses

Just a reminder from Part 3, section 3 of what it took to implement a /comics call to the Marvel Comic API:

The API returns data wrapped into a generic response object containing request metadata: MarvelApiResponse<T> with a final T data field, which was created in Part 2, section 3. Well, retrofit covers this case as well.

For a new MarvelComicsApi implementation:

the generated .g.dart file contains:

Pretty similar to what was previously written manually, isn’t it? The generated code contains MarvelApiResponse<...>.fromJson() call on line 16 with two parameters, as intended. Even more, the inner type of MarvelApiResponse is also generic, and MarvelPaginatedList<...>.fromJson() is also called with two parameters on line 18. Lastly, the inner type of MarvelPaginatedList is MarvelComic, so MarvelComic.fromJson() is called on line 20.

Conclusion

We now have prepared a retrofit-based implementation of the MarvelApi used to perform requests to Marvel Comic API.

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

Read on Part 6: REST API requests with retrofit. 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