Keywords: Flutter | HTTP_POST | URL_Encoding
Abstract: This article provides an in-depth exploration of sending application/x-www-form-urlencoded POST requests in Flutter applications. By analyzing the root causes of common errors like 'type _InternalLinkedHashMap<String, dynamic> is not a subtype of type String', it systematically introduces the complete workflow of JSON serialization, URL encoding, and parameter naming. The paper compares implementation approaches using both dart:io HttpClient and package:http, explains form data encoding mechanisms in detail, and provides ready-to-use code examples.
Problem Background and Error Analysis
When attempting to send POST requests with application/x-www-form-urlencoded content type in Flutter development, developers often encounter type conversion errors. Specifically: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String' in type cast. The fundamental cause of this error is that the HTTP request's body parameter expects a string type, while developers directly pass Map objects.
Core Steps of the Solution
To correctly send URL-encoded POST requests, three key steps must be completed: first convert the JSON map to a string, then perform URL encoding, and finally assign a name to the POST parameter. This process ensures data can be transmitted to the server in standard format.
Implementation Using dart:io HttpClient
Here is a complete implementation example based on the dart:io library:
Future<HttpClientResponse> sendUrlEncodedRequest() async {
Map<String, dynamic> jsonMap = {
'homeTeam': {'team': 'Team A'},
'awayTeam': {'team': 'Team B'},
};
String jsonString = json.encode(jsonMap);
String paramName = 'matchData';
String formBody = paramName + '=' + Uri.encodeQueryComponent(jsonString);
List<int> bodyBytes = utf8.encode(formBody);
HttpClientRequest request = await HttpClient().post('example.com', 80, '/api/endpoint');
request.headers.set('Content-Length', bodyBytes.length.toString());
request.headers.set('Content-Type', 'application/x-www-form-urlencoded');
request.add(bodyBytes);
return await request.close();
}Optimized Solution Using package:http
For a more concise implementation, the package:http library provides more convenient APIs. The key point is that the body parameter must be of type Map<String, String>:
Map<String, String> requestBody = {
'name': 'doodle',
'color': 'blue',
'teamJson': json.encode({
'homeTeam': {'team': 'Team A'},
'awayTeam': {'team': 'Team B'},
}),
};
Response response = await post(
Uri.parse('https://api.example.com/endpoint'),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: requestBody,
);In-Depth Analysis of Encoding Mechanisms
The package:http library automatically handles URL encoding and UTF-8 encoding processes. When using a Map as the body, the library internally calls Uri.encodeQueryComponent to encode key-value pairs and automatically sets the Content-Length header. The network transmission data generated by the above code is: name=doodle&color=blue&teamJson=%7B%22homeTeam%22%3A%7B%22team%22%3A%22Team+A%22%7D%2C%22awayTeam%22%3A%7B%22team%22%3A%22Team+B%22%7D%7D, where %7B, %22, etc., are URL-encoded representations of JSON special characters.
Comparison of Parameter Naming Strategies
Developers can choose different parameter organization methods based on API requirements. Dispersed parameter approach:
Map<String, String> dispersedBody = {
'homeTeam': json.encode({'team': 'Team A'}),
'awayTeam': json.encode({'team': 'Team B'}),
};Generates: homeTeam=%7B%22team%22%3A%22Team+A%22%7D&awayTeam=%7B%22team%22%3A%22Team+B%22%7D. The centralized parameter approach treats the entire JSON object as a single parameter value, making it more suitable for complex nested data structures.
Error Handling and Best Practices
In actual development, it's recommended to add exception handling mechanisms:
try {
Response response = await post(
Uri.parse(url),
body: requestBody,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
);
if (response.statusCode == 200) {
// Process successful response
var responseData = json.decode(response.body);
} else {
// Handle error status codes
throw Exception('Request failed with status: ${response.statusCode}');
}
} catch (e) {
// Handle network exceptions
print('Request error: $e');
}Additionally, it's advisable to use connection timeout settings in production environments and consider using third-party libraries like Dio to simplify complex HTTP operations.