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.
dart:io library - Dart API
dart:io library API docs, for the Dart programming language.

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) {
        ..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) {
      return '';
  }).catchError((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;
      (String data) {
      onDone: () => completer.complete(contents.toString()),
      cancelOnError: true,
      onError: (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:

get method - Client class - http library - Dart API
API docs for the get method from the Client class, for the Dart programming language.
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.