Making Flutter and REST API Work Together — Code Review (Part 5)

Alex Josef Bigler
Full Struggle Developer
4 min readMar 25, 2023

If we look at our ApiDataProvider, we can see an unpleasant duplication of code and potentially even more duplication of code in the future. The more requests we make to the API, the more code will be copied with the creation of the client and handling of the response.

The following posts are suggested reading for this post to get you started:

A few final touches

The first thing we will do is remove the line:

Client client = Client();

We’ll create an alias for the http library, and since only one class is exported from this library, the compiler will figure out what we want from it (in essence, the compiler will create the Client where it is needed).

import 'package:http/http.dart' as http;

final response = await http.get();

Next, we will move the response processing to a separate method, because regardless of the type of request, the response will always be processed the same way.

Note that we should handle the main types of error codes: 200, 400, 401, 403, 404, 500, 502, 503, as well as 201, which was used in the previous article — successful creation of a new resource on the server.

- 200, 201: success, return response body

- 400: bad request, throw Exception(“Bad request”)

- 401: unauthorized, throw Exception(“Unauthorized”)

- 403: forbidden, throw Exception(“Forbidden”)

- 404: not found, throw Exception(“Not found”)

- 405: method not allowed, throw Exception(“Method not allowed”)

- 500: internal server error, throw Exception(“Internal server error”)

- 502: bad gateway, throw Exception(“Bad gateway”)

- 503: service unavailable, throw Exception(“Service unavailable”)

- all other status codes: unknown response status, throw Exception(“Unknown response status”)

The last four errors are not dependent on the mobile application developer, but it is still your responsibility to respond to users. Therefore, it is worth adding a pop-up window in the style of “EVERYTHING IS BROKEN! We will be back soon.” Later and stronger.

The method looks like this:

String processResponse(Response response) {
if (response.statusCode == 200 || response.statusCode == 201) {
return response.body;
} else if (response.statusCode == 400) {
throw Exception("Bad request");
} else if (response.statusCode == 401) {
throw Exception("Unauthorized");
} else if (response.statusCode == 403) {
throw Exception("Forbidden");
} else if (response.statusCode == 404) {
throw Exception("Not found");
} else if (response.statusCode == 405) {
throw Exception("Method not allowed");
} else if (response.statusCode == 500) {
throw Exception("Internal server error");
} else if (response.statusCode == 502) {
throw Exception("Bad gateway");
} else if (response.statusCode == 503) {
throw Exception("Service unavailable");
} else {
throw Exception("Unknown response status");
}
}

This is a general list of the main statuses that can be returned in response to an HTTP request. Specific statuses may depend on the specific web service or application.

We also need to modify our API methods to handle the response in the new way:

var result = ForecastResponse.fromJson(jsonDecode(processResponse(response)));

Here is the final version of our api_data_provider.dart file:

In conclusion

Code can be reviewed almost endlessly, and the main task of a code review is to prevent the project from growing to millions of lines, while still making the code readable for programmers of any level:

  • Do not use libraries just for the sake of using libraries.
  • Don’t create unnecessary entities (following Occam’s razor).

For instance, in the current scenario, we have the option to move the entire logic for handling and dispatching requests to an abstract class and then create a weather_data_provider that inherits from the abstract class. However, since we don’t have any plans to change the data source from REST to a database, creating such a level of abstraction isn’t necessary. Additionally, since we only have two requests, it may not be worthwhile to introduce the complexity of an abstract class.

We could alternatively leverage a library like GetX to handle requests and eliminate the need to write custom processing code. However, in doing so, we would inherit any bugs or issues associated with the library. But you can see, managing server http requests isn’t as difficult as it may seem.

In a future article, I will explore how to develop deserialization classes using the server’s Swagger documentation.

Subscribe and don’t miss new materials!

--

--

Alex Josef Bigler
Full Struggle Developer

Enthusiast of new technologies, entrepreneur, and researcher. Writing about IT, economics, and other stuff. Exploring the world through the lens of data.