Flutter Web + Wasm: What, Why & Deployment
WebAssembly compilation, performance gains, current limitations, deployment workflow
Open interactive version (quiz + challenge)Real-world analogy
What is it?
Flutter Web with WebAssembly compilation is the current frontier of Flutter Web performance. It represents the maturation of Flutter's web support from an experimental curiosity to a genuinely competitive option for complex interactive web applications — while honest about its limitations for SEO-dependent use cases.
Real-world relevance
The SaaS collaboration platform's web dashboard migrates from CanvasKit to Wasm after Flutter 3.22 ships. The complex real-time collaborative canvas (whiteboard feature) benefits from 2-3x faster DOM-equivalent computation. The team updates their Firebase Hosting configuration to add COOP/COEP headers. They add package:web migrations for two plugins. Browser analytics confirm 95%+ of their enterprise users are on Chrome 119+ or Firefox 120+.
Key points
- What is WebAssembly — WebAssembly (Wasm) is a binary instruction format for a stack-based virtual machine. It runs in all modern browsers at near-native speed. Wasm code is compact, fast to parse, and executes in a sandboxed environment. It was designed to complement JavaScript, not replace it.
- Dart-to-Wasm Compilation — dart2wasm compiles Dart code directly to WebAssembly bytecode — bypassing the JavaScript compilation step (dart2js). The result executes in the browser's Wasm runtime instead of the JS engine. Requires Dart 3.3+ and Flutter 3.22+.
- Why Wasm Matters for Flutter Web — Previous Flutter Web used dart2js (Dart compiled to JavaScript). JavaScript is interpreted/JIT-compiled — performance is inconsistent. Wasm is AOT-compiled at the browser level — execution is predictable, faster, especially for Dart's type system and object model.
- Performance Improvements — Benchmarks show 2-3x faster execution for compute-heavy code compared to dart2js. Startup time improves compared to CanvasKit (smaller initial payload, no Skia Wasm loading). Rendering pipeline stays canvas-based — same visual output as CanvasKit.
- Browser Compatibility — Wasm GC (Garbage Collection) extension is required — supported in Chrome 119+ (Oct 2023) and Firefox 120+ (Nov 2023). Safari added support in Safari 18 (Sep 2024). IE is not supported. Check Can I Use for current coverage. Flutter falls back to CanvasKit for unsupported browsers.
- Current Limitations — dart:mirrors is unsupported in Wasm (also true for dart2js in release mode). JS interop requires package:web (not dart:html — deprecated for Wasm). Some Flutter plugins using dart:html break on Wasm. Platform-specific code needs migration to package:web API.
- JS Interop Migration — dart:html is deprecated for Flutter Wasm. Use package:web (the new Dart web API package) which maps to browser IDL types. Also use dart:js_interop for calling JavaScript from Dart. This is a breaking change for plugins — check pub.dev compatibility.
- Deployment Workflow — flutter build web --wasm generates a build/web directory with Wasm artifacts (.wasm file, JS bootstrap). Deploy to any static host (Firebase Hosting, Vercel, GitHub Pages, AWS S3+CloudFront). Serve with correct MIME type for .wasm files (application/wasm). Enable COOP/COEP headers for SharedArrayBuffer.
- Fallback Strategy — Flutter's build system generates both Wasm and CanvasKit bundles and auto-selects based on browser capabilities. The flutter_bootstrap.js file contains the feature detection logic — you do not need to manually manage fallbacks.
- When to Choose Flutter Web + Wasm — Complex interactive web apps (data visualisation, design tools, collaborative editors) that need maximum performance. Teams already using Flutter for mobile who want a high-performance web target. When the user base is known to use modern browsers (internal tools, enterprise apps). NOT for SEO-critical public websites.
Code example
// === BUILD COMMANDS ===
// Build with Wasm (Flutter 3.22+)
// flutter build web --wasm
// Build with specific renderer fallback
// flutter build web --wasm --dart-define=FLUTTER_WEB_AUTO_DETECT=true
// Serve locally with correct Wasm MIME type
// flutter run -d chrome --wasm
// === REQUIRED RESPONSE HEADERS (firebase.json) ===
/*
{
"hosting": {
"headers": [
{
"source": "**",
"headers": [
{ "key": "Cross-Origin-Opener-Policy", "value": "same-origin" },
{ "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" }
]
},
{
"key": "Content-Type",
"value": "application/wasm",
"source": "**.wasm"
}
]
}
}
*/
// === JS INTEROP — MIGRATING FROM dart:html TO package:web ===
// OLD (dart:html — does NOT work with Wasm):
import 'dart:html' as html;
void downloadFile(String url, String filename) {
final anchor = html.AnchorElement(href: url)
..setAttribute('download', filename)
..click();
}
// NEW (package:web + dart:js_interop — Wasm-compatible):
import 'package:web/web.dart' as web;
import 'dart:js_interop';
void downloadFile(String url, String filename) {
final anchor = web.document.createElement('a') as web.HTMLAnchorElement;
anchor.href = url;
anchor.download = filename;
web.document.body!.append(anchor);
anchor.click();
anchor.remove();
}
// === CONDITIONAL IMPORTS — SUPPORT BOTH MOBILE AND WEB ===
// web_utils_stub.dart (for non-web platforms)
void downloadFile(String url, String filename) {
throw UnsupportedError('downloadFile only supported on web');
}
// web_utils_web.dart (for web platforms)
import 'package:web/web.dart' as web;
void downloadFile(String url, String filename) {
// ... web implementation
}
// In your service:
import 'web_utils_stub.dart'
if (dart.library.js_interop) 'web_utils_web.dart';
// === CHECKING WASM SUPPORT IN FLUTTER BOOTSTRAP ===
/*
flutter_bootstrap.js auto-detects Wasm GC support:
- if (WebAssembly.validate(wasmGcFeatureDetectionBytes)) -> load .wasm
- else -> fall back to CanvasKit JS bundle
You can read which path was taken:
*/
import 'dart:js_interop';
@JS('window.__flutter_loader_initialized_with_wasm')
external bool get isWasmEnabled;
// === PERFORMANCE MEASUREMENT ===
class PerformanceTracker {
static void measureFrameTime() {
// Compare Wasm vs CanvasKit rendering
final stopwatch = Stopwatch()..start();
WidgetsBinding.instance.addPostFrameCallback((_) {
final frameTime = stopwatch.elapsedMicroseconds;
analytics.logEvent('frame_time', parameters: {
'microseconds': frameTime,
'renderer': isWasmEnabled ? 'wasm' : 'canvaskit',
});
});
}
}
// === PUBSPEC.YAML — REQUIRED DEPENDENCIES ===
/*
dependencies:
flutter:
sdk: flutter
web: ^1.0.0 # New Wasm-compatible web APIs (replaces dart:html)
dev_dependencies:
flutter_test:
sdk: flutter
*/Line-by-line walkthrough
- 1. flutter build web --wasm — triggers dart2wasm compiler instead of dart2js; generates .wasm file alongside JS bootstrap
- 2. COOP/COEP headers in firebase.json — enables SharedArrayBuffer required by Wasm threading; must be set at hosting level
- 3. dart:html AnchorElement — the OLD approach; works with dart2js but fails to compile or misbehaves with dart2wasm
- 4. package:web HTMLAnchorElement — the NEW approach; uses Wasm-compatible IDL bindings generated from browser specs
- 5. conditional imports (stub/web pattern) — allows same Dart code to work on mobile (stub) and web (web implementation) by selecting the correct file at compile time
- 6. @JS() annotation with external keyword — dart:js_interop binding; tells the Dart compiler this function is implemented in JavaScript
- 7. flutter_bootstrap.js auto-detection — checks for Wasm GC support, loads .wasm bundle if available, falls back to CanvasKit JS otherwise
- 8. WidgetsBinding.addPostFrameCallback — fires after each frame completes; used here to measure actual frame render time and log renderer type
Spot the bug
// Flutter Web Wasm app — file download utility
import 'dart:html' as html;
class FileDownloadService {
void downloadReport(String url) {
final link = html.document.createElement('a') as html.AnchorElement;
link.href = url;
link.download = 'report.pdf';
html.document.body!.children.add(link);
link.click();
html.document.body!.children.remove(link);
}
}Need a hint?
Show answer
Explain like I'm 5
Fun fact
Hands-on challenge
More resources
- Flutter Web Wasm — official docs (Flutter Docs)
- package:web (pub.dev)
- dart:js_interop (Dart Docs)
- WebAssembly — W3C spec (W3C)
- Can I use — Wasm GC (caniuse.com)