Fixing `Caught error: Unsupported operation: Platform._version` when using HttpClient in Flutter Web
While porting my Flutter mobile app to Web, I ran into this exception:
Caught error: Unsupported operation: Platform._version
It originated from dart-sdk/lib/_http/http.dart, more precisely as soon as I instantiate dart io's HttpClient.
Starting from dart io's HttpClient
After reading a few StackOverflow posts, it turns out that there are two ways to make HTTP requests. One with dart io's HttpClient, and one with the http package. I was using the former. It turns out dart io is for non Web apps only:
File, socket, HTTP, and other I/O support for non-web applications.
Important: Browser-based apps can't use this library.
It's interesting that it works for all platforms (server, mobile, desktop) but web.
My code using HttpClient looked roughly like so:
import 'dart:io';
client = new HttpClient();
Future<String> downloadResponse(HttpClient client, Uri url) {
return client.getUrl(url).then((HttpClientRequest request) {
request.headers
..add('accept', 'text/html,application/xhtml+xml,application/xml')
..add('accept-language', 'en-US,en');
return request.close();
}).then((HttpClientResponse response) async {
try {
final textResponse = await readResponse(response);
return textResponse;
} catch (e) {
print(e);
return '';
}
}).catchError((e) {
print(e);
return '';
});
}
// From https://stackoverflow.com/questions/27808848/retrieving-the-response-body-from-an-httpclientresponse
Future<String> readResponse(HttpClientResponse response) {
var completer = new Completer<String>();
var contents = new StringBuffer();
Converter<List<int>, String> decoder = utf8.decoder;
response.transform(decoder).listen(
(String data) {
contents.write(data);
},
onDone: () => completer.complete(contents.toString()),
cancelOnError: true,
onError: (e) {
print(e);
completer.completeError(Exception("Bad encoding"));
});
return completer.future;
}
To be honest, I have no idea why the code was this complicated, using a decoder, etc.
http package
http supports all platforms, web included.
Usage seems straightforward too. Btw, it supports two modes. One-off requests, and initializing a client, then reusing repeatedly:
If you're making multiple requests to the same server, you can keep open a persistent connection
So here's how the code above should look like using http:
import 'package:http/http.dart';
client = Client();
Future<String> downloadResponse(Client client, Uri url) async {
final headers = {
'accept': 'text/html,application/xhtml+xml,application/xml',
'accept-language': 'en-US,en'
}
final response = await client.get(url, headers: headers);
return response.body;
}
I am surprised at how concise the code is, and how I was able to keep the same signatures. As a result, migrating unit tests was a breeze as well.