Making Flutter and REST API Work Together — Sending POST Request (Part 4)
Support for sending POST requests is critical for mobile applications because it allows for sending data to a server for processing, creating, updating, or deleting records in a database, and many other operations that are essential for most modern applications.
The following posts are suggested reading for this post to get you started:
- 👉 Part 1: Creating API Provider
- 👉 Part 2: Debugging with Unit Tests
- 👉 Part 3: Preventing Application Freezing
Now that we understand how to send a GET request and work with the response, how do we send JSON in the body of a POST request?
All we need to do is serialize the object into JSON.
From words to action
Note: This material is created to demonstrate the deserialization process itself, rather than the process of sending a POST request, so this part will be indirectly touched upon. However, in the article, we will deserialize all commonly used objects.
To practice sending POST requests and not break anyone’s real server, we will use the service JSONPlaceholder.
If you want to deploy such a server yourself, you can read my article here 👈.
In the api_data_provider.dart
file, we add the ForecastContract
class. We will deserialize a string
, an object
, an array of objects
, and an array of numbers
. Next, line by line:
- Line 9 — This is the opposite of the
fromJson
method. There, we had aMap<String, dynamic>
input, and here, our task is to obtain such an array as output. - Line 12— We declare a class for nested object, which we will also deserialize, and we also add the toJson method to it.
In our ApiDataProvider
class, we will add a POST method and go through it line by line:
The differences from the GET method are minimal.
- Line 7— deserialization.
- Line 8— we change the method from GET to POST.
Now, to control the success of the request, we check for a status code of 201 instead of 200, which means a successful creation of a new resource on the server.
Also, in addition to the id
field indicating the identifier of the new entity, the response should return the body of our request.
To handle it, we write several classes:
class ForecastContractResponse {
final String title;
final List<int> nums;
final ForecastContractResponseItem item;
final List<ForecastContractResponseItem> items;
final int id;
ForecastContractResponse(this.title, this.nums, this.item, this.items, this.id);
factory ForecastContractResponse.fromJson(Map<String, dynamic> json) => ForecastContractResponse(
json["title"],
List<int>.from(json["nums"].map((x) => x)),
ForecastContractResponseItem.fromJson(json["item"]),
List<ForecastContractResponseItem>.from(json["items"].map((x) => ForecastContractResponseItem.fromJson(x))),
json["id"],
);
Map<String, dynamic> toJson() => {
"title": title,
"nums": List<dynamic>.from(nums.map((x) => x)),
"item": item.toJson(),
"items": List<dynamic>.from(items.map((x) => x.toJson())),
"id": id,
};
}
class ForecastContractResponseItem {
ForecastContractResponseItem({
required this.title,
});
final String title;
factory ForecastContractResponseItem.fromJson(Map<String, dynamic> json) => ForecastContractResponseItem(
title: json["title"],
);
Map<String, dynamic> toJson() => {
"title": title,
};
}
You may notice that items are collected into arrays using the
map()
iterator. Yes, this method is slower than what we did before (and I complained a bit about it in the first article), but it’s just my professional deformation, and who cares about that anyway.
We will add a new test in the api_data_provider_test.dart
file to verify the results of the POST request. The JSONPlaceholder service should return not only the 201
status code but also the body of our request and the id
field. Therefore, we will check for the presence of the id
field, which indicates that our request was processed and some new resource was created on the server:
test('POST contract was encoded', () async {
final provider = ApiDataProvider();
var result = await provider.postWeather(ForecastContract('test', ForecastContractItem('test'), [ForecastContractItem('111')], [1, 2]));
expect(result.id, closeTo(100, 500));
});
At the breakpoint in the postWeather
method, we can see our deserialized object:
If we remove the backslashes and format the string (for example using https://jsoneditoronline.org), we get the following JSON:
{
"title": "test",
"nums": [
1,
2
],
"item": {
"title": "test"
},
"items": [
{
"title": "111"
}
]
}
Important: We remove the slashes because they indicate that a special character follows. In JSON, quotes are essential, but in Dart, quotes indicate the beginning and end of a string. Without the slash, the JSON would break at the first quote.
Instead of the Conclusion
The test, by the way, also completed without errors:
And that means we can go have a coffee and think about important questions like how to make something more beautiful, simple, and easy to maintain in the future from this code that was written in 5 minutes. By the way, this is exactly what the next article will be about.
Subscribe and don’t miss new materials!
- 👉 Read Part 5: Code Review
- 👉 Read Part 6: Building Dart Types from Swagger/OpenAPI Schemas
- 👉 Read Part 7: Efficient Pagination
- 👉 Read Part 8: Efficient Data Merging
- 👉 Read Part 9: Optimizing Search
- 👉 Read Part 10: Implementing Filtering
- 👉 Read Part 11: Seamless Data Caching Integration