Skip to content

Dart SDK libraries and Flutter for web #39998

Closed as not planned
Closed as not planned

Description

@jonahwilliams

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:io provides a reasonable set of functionality that we currently rely on in the framework. Specifically, we use the HttpClient to make network requests, Platform to detect the initial non-overridden platform, and File to transfer bytes across certain compute boundaries, like Isolates and through PlatformChannels. 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:isolate and dart: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 File in 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.

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).

cc @Hixie @tvolkert @goderbauer @ferhatb

Activity

  1. yjbanov commented on Sep 6, 2019

    @yjbanov
    Contributor

    +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:io is available, and simply removing it from the Web will make it harder to add Web support to existing plugins.

  2. tvolkert commented on Sep 6, 2019

    @tvolkert
    Contributor

    I recommended to @jonahwilliams that he put the options with pros and cons in a Google doc to better facilitate discussion.

  3. added this to the milestone on Sep 11, 2019
  4. harryterkelsen commented on Sep 12, 2019

    @harryterkelsen
    Contributor

    Web plugin authors are already hitting issues where their plugin implementations which import dart:html are 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-specific dart: library imports in web plugin implementations.

  5. yjbanov commented on Sep 13, 2019

    @yjbanov
    Contributor

    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?

  6. jonahwilliams commented on Sep 13, 2019

    @jonahwilliams
    ContributorAuthor

    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?

  7. devoncarew commented on Sep 13, 2019

    @devoncarew
    Contributor

    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-specific dart: library imports in web plugin implementations.

    This is feasible, and would ~match the behavior users see at runtime.

    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?

    It's something to consider, but it would add a lot of complexity; I'd prefer other solutions (like the lint one above).

    I think we could move a bit more quickly on the issue if we sat down and chatted for a bit sometime next week?

    sgtm

  8. devoncarew commented on Sep 13, 2019

    @devoncarew
    Contributor

    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:html or js interop.

  9. sh0umik commented on Oct 1, 2019

    @sh0umik

    Lot of packages depend on dart:io surely something need to be done. Also there is no clear documentation on how to skip building for those files

    transitive libraries have sdk dependencies that not supported on this platform:
    mqtt_client|lib/mqtt_client.dart
    

    Lets 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

  10. yjbanov commented on Oct 1, 2019

    @yjbanov
    Contributor

    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-specific dart: library imports in web plugin implementations.

    This is feasible, and would ~match the behavior users see at runtime.

    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 in dart:ui/package:flutter and in dart:html.

    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?

    It's something to consider, but it would add a lot of complexity; I'd prefer other solutions (like the lint one above).

    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.

    I think we could move a bit more quickly on the issue if we sat down and chatted for a bit sometime next week?

    Has this chat happened yet? Any outcomes? If not, I'd like to participate.

  11. devoncarew commented on Oct 2, 2019

    @devoncarew
    Contributor

    We did discuss this; specifically, the use of dart:html for Flutter web apps and plugins.

    There's more details here: #35588 (comment), but we decided to add dart:html (and dart:js and dart: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).

  12. NicolasDionB commented on Oct 2, 2019

    @NicolasDionB

    @devoncarew That's good news! What about other packages like dart:web_audio?

  13. 22 remaining items

  14. len8657 commented on Dec 10, 2020

    @len8657
  15. NicolasDionB commented on Dec 10, 2020

    @NicolasDionB

    @len8657 If you read the original post, you can see that there is an if statement that you can use in your imports, as follows:

    import 'b.dart'
      if (dart.library.io) 'c.dart'
    
    void main() {
      bar(1); // No error
    }
    

    With this technique, one could check with on the html library and make a conditional import specific for web:

    import 'my_class.dart'
      if (dart.library.html) 'my_class_web.dart'
    

    We used this in our project and it works like a charm!

  16. adigujjar commented on Feb 18, 2021

    @adigujjar

    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

  17. nicksnz3k commented on Feb 18, 2021

    @nicksnz3k

    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 new _http._HttpClient.new (http://localhost:65025/dart_sdk.js:186683:28)

    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();
    }
    `

  18. github-actions commented on Jun 4, 2025

    @github-actions

    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 -v and a minimal reproduction of the issue.

  19. locked as resolved and limited conversation to collaborators on Jun 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listcustomer: crowdAffects or could affect many people, though not necessarily a specific customer.dependency: dartDart team may need to help usframeworkflutter/packages/flutter repository. See also f: labels.platform-webWeb applications specificallyteam-webOwned by Web platform teamteam: skip-testAn issue used to track tests that are skipped.triaged-webTriaged by Web platform team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions