Chuyển đến nội dung chính

Shipping Flutter Web on WebAssembly: the migration, the browsers, the headers

WebAssembly is the largest single bet in Flutter’s 2026 roadmap, and 3.47 moves it forward again with experimental deferred loading. The Q2 2026 survey rates Web at 72% satisfaction — the second-lowest platform score — and load performance is a big part of why. Wasm is the intended fix.

But shipping Wasm today is not a flag. It is a migration, a browser support matrix with a large hole in it, and two HTTP headers that most people forget.

The build commands

# Development
flutter run -d chrome --wasm

# Production
flutter build web --wasm

# Production, with symbolication for error monitoring
flutter build web --wasm --source-maps

# Staging/QA — readable stack traces, ~47% larger binary
flutter build web --wasm --no-strip-wasm

Use --source-maps for anything you point Sentry or a similar service at; it produces main.dart.wasm.map. Use --no-strip-wasm only in staging — the size cost is real.

Flutter 3.47 also adds experimental deferred loading:

flutter build web --release --wasm --enable-wasm-deferred-loading

At the Dart layer this is the same capability that landed as a preview in Dart 3.13 (dart compile wasm -O2 --enable-deferred-loading), reported to give meaningful initial-page-load improvements over dart2js for large applications. For a big app this is the difference between Wasm being theoretically faster and actually faster in the metric users feel.

The browser matrix, and the iOS problem

Flutter’s Wasm output needs WasmGC. Support today:

BrowserStatus
Chromium / V8Supported, version 119+
FirefoxAnnounced, currently blocked by a known bug
SafariSupports WasmGC, but has a compatibility bug
iOS browsersCannot run it — every iOS browser uses WebKit

Read the last row again. It is not “Safari on iOS”; it is all browsers on iOS, because Apple requires WebKit. If your web traffic skews mobile, a meaningful share of your users will not execute your Wasm build at all.

Which is why the fallback matters: even with --wasm, Flutter still compiles to JavaScript. If WasmGC is not detected at runtime, the JS build runs. You are shipping both, and the browser picks. That is good for correctness and bad for anyone hoping Wasm reduces their deployment size.

To confirm which path a session took:

const isRunningWithWasm = bool.fromEnvironment('dart.tool.dart2wasm');

Log it. Otherwise you will be optimising a code path most of your users never hit.

The actual migration: get off dart:html

This is the work. Wasm will not compile if your app imports unsupported web libraries.

LegacyReplacement
dart:htmlpackage:web
dart:js, package:jsdart:js_interop

Flutter gives you an early warning without needing a Wasm build at all — running plain flutter build web performs a Wasm dry run:

Wasm dry run failed:
Found incompatibilities with WebAssembly.

package:my_app/main.dart 1:1 - dart:html unsupported (0)

Run that today, even if you have no Wasm plans. It is a free audit of your dependency tree.

When a full compilation fails, ignore the stack trace and find the Context tree — it names the package chain that pulled in the incompatible library:

Context: The unavailable library 'dart:html' is imported through these packages:
    main.dart => package:my_app => dart:html

That is usually a transitive dependency, not your code. For a gradual migration, conditional imports let both worlds coexist:

import 'fallback.dart'
  if (dart.library.js) 'legacy_web_interop.dart'
  if (dart.library.js_interop) 'wasm_web_interop.dart';

The two headers everyone forgets

Flutter Wasm apps can render across multiple threads — but only if your server sends the right cross-origin headers:

HeaderValue
Cross-Origin-Embedder-Policycredentialless or require-corp
Cross-Origin-Opener-Policysame-origin

Without both, multi-threading silently does not happen. Your app works; it is just slower than the benchmark you read. Check them in your CDN or reverse proxy config, not just in local dev — this is a classic “fast on localhost, slow in production” trap.

Note that require-corp will break embedded third-party resources that do not opt in via CORP headers. If you embed a lot of external content, credentialless is usually the pragmatic choice.

Runtime differences worth knowing

package:web and dart:js_interop under Wasm are not bit-identical to the JS backend:

  • is / as checks behave differently on JS interop types
  • Zone propagation in callbacks differs

Neither breaks a typical app, but both will bite code that does clever things with type checks across the interop boundary. Test your interop layer specifically rather than assuming parity.

Your rollout plan

  1. Run flutter build web today and read the Wasm dry run output. That is your migration backlog.
  2. Update web/index.html to the current Flutter initialization, or regenerate with flutter create . --platforms web.
  3. Migrate your own code from dart:html to package:web and from dart:js to dart:js_interop.
  4. Chase the transitive offenders named in the Context tree. File issues on dependencies that still import dart:html.
  5. Configure COOP and COEP headers in your production serving layer, then verify them with the network tab.
  6. Build with --wasm --source-maps and wire the map file into your error monitoring.
  7. Log dart.tool.dart2wasm and measure what fraction of real sessions actually get the Wasm path.
  8. Try --enable-wasm-deferred-loading if your bundle is large, and measure initial page load rather than total size.

The bottom line

Wasm is genuinely faster, and the roadmap points at it becoming the default. But today it is an additive build, not a replacement: you ship JS as well, iOS users get the JS path regardless, and the performance win depends on two headers most teams have not set. The single highest-value thing you can do this week costs nothing — run flutter build web and read the dry-run output. Whether or not you ship Wasm this quarter, that list of dart:html dependencies is a debt you will pay eventually.


Originally published on FlutterCook. Read the latest version there — that copy is the one kept up to date.

Nhận xét

Bài đăng phổ biến từ blog này

Thiết kế giao diện với DotNetBar (Phần 1)

Đây là phiên bản DotNetBar hỗ trợ C# và Visual Basic https://www.dropbox.com/s/wx80jpvgnlrmtux/DotNetBar.rar  , phiên bản này hỗ trợ giao diện Metro cực kỳ “dễ thương” Các bạn load về và cài đặt, khi cài đặt xong sẽ có source code mẫu của tất cả các control. Để sử dụng được các control của DotNetBar các bạn nhớ add item vào controls box. Thiết kế giao diện với DotNetBar, giao diện sẽ rất đẹp. Link các video hướng dẫn chi tiết cách sử dụng và coding: http://www.devcomponents.com/dotnetbar/movies.aspx Hiện tại DotNetBar có rất nhiều công cụ cực mạnh, trong đó có 3 công cụ dưới đây: DotNetBar for Windows Forms Requires with Visual Studio 2003, 2005, 2008, 2010 or 2012.   DotNetBar for WPF Requires with Visual Studio 2010 or 2012 and Windows Presentation Foundation.   DotNetBar for Silverlight Requires with Visual Studio 2010 or 2012 and Silverlight. Dưới đây là một số hình ảnh về các control trong DotnetBar.   Metro User Interface  controls with Metro Tiles, toolba...

5 concepts every Flutter dev should know

  Phụ lục: State management architecture Testing IDE Shortcuts Platform channel Maintaining a project Tôi đã làm việc với Flagship trong một thời gian dài, và đây là những điều mà tôi phát hiện ra là điều cần phải có đối với bất kỳ nhà phát triển Flagship nào, về tổng thể nó sẽ khiến bạn trở thành một nhà phát triển Flagship giỏi trong thời gian dài. 1. State management architecture Đây là một trong những chủ đề quan trọng nhất trong cộng đồng thiết bị rung, nó khá quan trọng nếu bạn muốn duy trì một dự án rung kích thước trung bình hoặc lớn. Nó sẽ giúp tạo một dự án suôn sẻ và thêm các tính năng mới một cách hoàn hảo.  2. Testing Đây là một chủ đề duy nhất mà tôi không hiểu tại sao nó lại quan trọng trước đó trong sự nghiệp của tôi, nhưng khi tôi tiến lên trong sự nghiệp của mình và có kinh nghiệm với nhiều dự án và vấn đề xảy ra trong môi trường sản xuất. Tôi đã nhận ra một cách khó khăn, tại sao điều này lại quan trọng như vậy. Nếu bạn vẫn muốn có thêm lý do để cân nhắc thử...

Announcing Flutter 2

  Phụ lục: Flutter on the web Flutter 2 on desktops, foldables, and embedded devices The growing Flutter ecosystem Dart: The secret sauce behind Flutter Flutter 2: Available now Hôm nay, chúng tôi sẽ công bố Flutter 2: một bản nâng cấp lớn cho Flutter cho phép các nhà phát triển tạo các ứng dụng đẹp, nhanh chóng và di động cho bất kỳ nền tảng nào. Với Flutter 2, bạn có thể sử dụng cùng một cơ sở mã để gửi các ứng dụng gốc cho năm hệ điều hành: IOS, Android, Windows, macOS và Linux; cũng như trải nghiệm web nhắm mục tiêu các trình duyệt như Chrome, Firefox, Safari hoặc Edge. Flutter thậm chí có thể được nhúng vào ô tô, TV và thiết bị gia dụng thông minh, mang đến trải nghiệm di động và lan tỏa nhất cho thế giới điện toán xung quanh. Mục tiêu của chúng tôi là thay đổi cơ bản cách các nhà phát triển nghĩ về việc xây dựng ứng dụng, bắt đầu không phải với nền tảng bạn đang nhắm mục tiêu mà là với trải nghiệm bạn muốn tạo. Flutter cho phép bạn tạo ra những trải nghiệm tuyệt đẹp trong đó ...