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.