Repository navigation
Description
Background
An unsupported library is either missing an implementation in the platform's libraries.json file, or has an implementation where all members throw when called.
The only mechanism for conditionally including code is config-specific imports and exports. The mechanism for using these is the presence/absence of an *unsupported library
Specific Issues
-
dart:ioprovides a reasonable set of functionality that we currently rely on in the framework. Specifically, we use theHttpClientto make network requests,Platformto detect the initial non-overridden platform, andFileto transfer bytes across certain compute boundaries, likeIsolatesand throughPlatformChannels. See also Gallery uses Android styling in iOS Chrome #32220, [image_picker] Add support for web #36281, Move Platform (or similar) to dart:core dart-lang/sdk#35969 -
The existing dart4web tooling refuses to compile applications to the web that have an unsupported library in the transitive closure, including
dart:isolateanddart:io. Under this restriction, almost no current flutter applications will compile to the web unless all dependencies are rewritten to use config specific imports. See also Add an option to skip the platform check for specific packages dart-lang/build#2420. -
The analyzer does not support having multiple analysis summaries. Thus either dart libraries that exist are displayed as missing, or dart libraries that are missing are displayed as existing. See also Properly enable dart:html imports #35588
-
The analyzer is not capable of analyzing code that uses config specific imports beyond the default import case. Because it does not analyze other paths, the user is responsible for verifying that the interfaces are used in a compatible way. For example, an API incompatibility that will not be detected until compile time:
a.dart
import 'b.dart'
if (dart.library.io) 'c.dart'
void main() {
bar(1); // No error
}
b.dart
void bar(int x) { }
c.dart
import 'dart:io';
void bar(String x) { }
- The analyzer is not capable of analyzing code that uses config specific exports beyond the default export case. Config specific exports are required if a type from an unsupported library is a part of a public API. For example, to include
Filein an interface conditionally:
a.dart
export 'b.dart'
if (dart.library.io) 'c.dart'
b.dart
void foo(Object file) {}
c.dart
import 'dart:io';
void foo(File file) {}
This relies on the fact that it is always safe to substitute the two APIs, but the caller will not get any information about which implementation is used, or when to use it.
- Pub is not capable of distinguishing between a library that conditionally supports Web/Flutter/Flutter for Web/VM. Specifically see Properly enable dart:html imports #35588 (comment)
Library Delta
Due to the additional inclusion of web specific libraries that we can't currently remove, the delta between a Flutter for Mobile/Desktop and Flutter for Web is currently quite large. The complexity is entirely placed on our users to deal with:
| Library | Web | Mobile/Desktop |
|---|---|---|
| ui | ✔ | ✔ |
| async | ✔ | ✔ |
| core | ✔ | ✔ |
| collection | ✔ | ✔ |
| typed_data | ✔ | ✔ |
| developer | ✔ | ✔ |
| convert | ✔ | ✔ |
| math | ✔ | ✔ |
| wasm | x | ✔ |
| ffi | x | ✔ |
| io | x | ✔ |
| isolate | x | ✔ |
| js | ✔ | x |
| js_util | ✔ | x |
| html | ✔ | x |
| web_audio | ✔ | x |
| web_gl | ✔ | x |
| indexed_db | ✔ | x |
| web_sql | ✔ | x |
| svg | ✔ | x |
| mirrors | x | x |
The only way to write an app, plugin, or package that works across all platforms without heavy use of config specific imports/exports is to only touch libraries supported on all platforms. However this represents a lowest common denominator strategy, which is against the core principals of flutter.
Comparison to method/message channels
Flutter already deals with platform specific features using platform channels. An asynchronous message is sent to the platform side, where platform code may respond (for example, a camera), or it might go into the void. The application author can choose to ignore the error or make a requirement, but Flutter itself does not make that choice.
The introduction of the web and web libraries has put us in a situation where we are deciding for the application author.
Problems with dart:html
Due to the highly dynamic nature of the browser DOM, any usage of dart:html will transitively pull in almost all of the dart core libraries. These libraries are also somewhat intertwined with the implementation of dart2js, the production JavaScript compiler. This adds additional code size to all flutter applications, even though the only use case may be within the web implementation of dart:ui. The only alternative would be to use JavaScript interop, but this again pulls in dart:html transitively.
dart:html is also generated from a chrome IDL and may not be accurate for a given browser/version. It is somewhat equivalent to a code generated dart:android or dart:ios library.
Rock and a Hard Place
It seems as if we either need dramatically better support for the config specific import or other conditional compilation feature, or we need to standardize the set of dart SDK libraries (even if some methods are unimplemented).
Activity
yjbanov commentedon Sep 6, 2019
+1 We need a graceful solution to this problem. The web/vm dichotomy creates a barrier for plugin authors. The Flutter ecosystem grew under the assumption that
dart:iois available, and simply removing it from the Web will make it harder to add Web support to existing plugins.tvolkert commentedon Sep 6, 2019
I recommended to @jonahwilliams that he put the options with pros and cons in a Google doc to better facilitate discussion.
harryterkelsen commentedon Sep 12, 2019
Web plugin authors are already hitting issues where their plugin implementations which import
dart:htmlare giving analyzer errors and they just have to trust that it will work at compile time.@devoncarew mentioned that the Flutter Analyzer SDK is separate from the Flutter SDK. I think we should just dump every
dart:library in there temporarily so that users don't see errors while building their plugin packages. We can supplement it with a lint that only allows web-specificdart:library imports in web plugin implementations.yjbanov commentedon Sep 13, 2019
Would it be possible to configure the analyzer to use different sets of
dart:libraries depending on whether the file is part of a plugin or part of the app? Maybe we could run two analyzers and have the IDE switch between the two depending on the file?jonahwilliams commentedon Sep 13, 2019
I'm concerned about leaking information to the mobile world. Ideally we would have a way to filter down the set of available libraries via some indication of web support, pubspec, or versioning. I think we could move a bit more quickly on the issue if we sat down and chatted for a bit sometime next week?
devoncarew commentedon Sep 13, 2019
This is feasible, and would ~match the behavior users see at runtime.
It's something to consider, but it would add a lot of complexity; I'd prefer other solutions (like the lint one above).
sgtm
devoncarew commentedon Sep 13, 2019
And - re: the items about config specific imports above - my working understanding is that user's won't use them when building plugin; they would be using
dart:htmlor js interop.sh0umik commentedon Oct 1, 2019
Lot of packages depend on
dart:iosurely something need to be done. Also there is no clear documentation on how to skip building for those filesLets say if want to build without mqtt_client. Building without this lib wont hurt for web but i want to keep the lib for flutter native
yjbanov commentedon Oct 1, 2019
In this mode what happens when a user attempts to auto-complete or find docs for the following classes:
Element,Window,Animation,Canvas,Point? These classes exist both indart:ui/package:flutterand indart:html.Definitely no need to introduce unnecessary complexity. However, if the world we're heading towards is one that offers multiple sets of
dart:*libraries in the same project, then we must make sure developers have the tools to navigate them fluently. If that requires more complexity in the tools, then that's a necessary complexity.Has this chat happened yet? Any outcomes? If not, I'd like to participate.
devoncarew commentedon Oct 2, 2019
We did discuss this; specifically, the use of
dart:htmlfor Flutter web apps and plugins.There's more details here: #35588 (comment), but we decided to add
dart:html(anddart:jsanddart:js_util) to the version of the Flutter SDK used for analysis, and create a lint which warns when dart:html is used in a context where those APIs won't be available at runtime (when the project is not a Flutter web project or a Flutter web plugin one).NicolasDionB commentedon Oct 2, 2019
@devoncarew That's good news! What about other packages like
dart:web_audio?22 remaining items
len8657 commentedon Dec 10, 2020
NicolasDionB commentedon Dec 10, 2020
@len8657 If you read the original post, you can see that there is an
ifstatement that you can use in your imports, as follows:With this technique, one could check with on the html library and make a conditional import specific for web:
We used this in our project and it works like a charm!
adigujjar commentedon Feb 18, 2021
I got this error when try to run on chrome :(
Error: Unsupported operation: Platform.version
at Object.throw [as throw] (http://localhost:65025/dart_sdk.js:4348:11)
at Function._version (http://localhost:65025/dart_sdk.js:55019:17)
at Function.get version [as version] (http://localhost:65025/dart_sdk.js:55093:27)
at get _version (http://localhost:65025/dart_sdk.js:54963:27)
at Function.desc.get [as _version] (http://localhost:65025/dart_sdk.js:4842:17)
at Function.get version [as version] (http://localhost:65025/dart_sdk.js:54937:26)
at Object._getHttpVersion (http://localhost:65025/dart_sdk.js:191712:31)
at new _http._HttpClient.new (http://localhost:65025/dart_sdk.js:186683:28)
at Function.new (http://localhost:65025/dart_sdk.js:181124:16)
at get _httpClient (http://localhost:65025/dart_sdk.js:191708:34)
at Function.desc.get [as _httpClient] (http://localhost:65025/dart_sdk.js:4842:17)
at Function.connect (http://localhost:65025/dart_sdk.js:191326:35)
at Function.connect (http://localhost:65025/dart_sdk.js:190033:35)
at Function.connect (http://localhost:65025/packages/web_socket_channel/io.dart.lib.js:76:94)
at get channel (http://localhost:65025/packages/magic_game/pages/playerGasture/TriangleShape.dart.lib.js:27016:36)
at Function.desc.get [as channel] (http://localhost:65025/dart_sdk.js:4873:17)
at Function.listenToSocket (http://localhost:65025/packages/magic_game/pages/playerGasture/TriangleShape.dart.lib.js:26961:48)
at main$ (http://localhost:65025/packages/magic_game/pages/playerGasture/TriangleShape.dart.lib.js:27047:33)
at main (http://localhost:65025/web_entrypoint.dart.lib.js:34:27)
at main.next ()
at http://localhost:65025/dart_sdk.js:37956:33
at _RootZone.runUnary (http://localhost:65025/dart_sdk.js:37810:58)
at _FutureListener.thenAwait.handleValue (http://localhost:65025/dart_sdk.js:32771:29)
at handleValueCallback (http://localhost:65025/dart_sdk.js:33319:49)
at Function._propagateToListeners (http://localhost:65025/dart_sdk.js:33357:17)
at async._AsyncCallbackEntry.new.callback (http://localhost:65025/dart_sdk.js:33082:27)
at Object._microtaskLoop (http://localhost:65025/dart_sdk.js:38071:13)
at _startMicrotaskLoop (http://localhost:65025/dart_sdk.js:38077:13)
at http://localhost:65025/dart_sdk.js:33574:9
nicksnz3k commentedon Feb 18, 2021
this is HttpClient error - need to choose right client for web and others
"http_client_base.dart"
`
import 'package:http/http.dart';
BaseClient createHttpClient() => throw UnsupportedError(
'Cannot create a client without dart:html or dart:io.');
`
"http_client_nonweb.dart"
`
import 'dart:io';
import 'package:http/http.dart';
import 'package:http/io_client.dart';
BaseClient createHttpClient() {
HttpClient client = new HttpClient();
client.connectionTimeout = new Duration(seconds: 30);
return IOClient(client);
}
`
"http_client_web.dart"
`
import 'package:http/browser_client.dart';
import 'package:http/http.dart';
BaseClient createHttpClient() => BrowserClient();
`
"WebApiClient.dart"
`
import 'dart:convert';
import 'package:http/http.dart';
import 'package:http/io_client.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:meta/meta.dart';
// ignore: uri_does_not_exist
import 'http_client_base.dart'
// ignore: uri_does_not_exist
if (dart.library.html) 'http_client_web.dart'
// ignore: uri_does_not_exist
if (dart.library.io) 'http_client_nonweb.dart';
class WebApiClient {
final BaseClient _client = createHttpClient();
}
`
corrected js zero bug and flutter#39998
github-actions commentedon Jun 4, 2025
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.