skip to content

Web & Desktop Targets

One Flutter codebase also builds for browsers, compiled to JavaScript or WebAssembly, and for Windows, macOS and Linux through native embedders. Interviewers probe what changes once you leave mobile.

part ofFlutteroverview, primer and where to startread it →
on this pageshow

explore

questions

12

Which renderers does Flutter 3.47 use on the web, and how does an app end up on CanvasKit or skwasm?

level: juniorimportance: must knowfreq 52%

answer

  1. both are Skia compiled to WebAssembly
  2. plain build web means dart2js
  3. --wasm adds a dart2wasm build
  4. the loader picks at page load
  5. HTML renderer removed, flag gone in 3.29

basics

~20 s

Flutter web draws with Skia in two builds: CanvasKit, used by the default JavaScript output of flutter build web, and skwasm, used when the app is built with --wasm and the browser can run it. The HTML renderer is gone.

solid answer

~40 s

Flutter 3.47 has two web renderers, both Skia compiled to WebAssembly and drawing into a canvas, not into HTML elements; Impeller is not used on the web. **CanvasKit** pairs with the JavaScript output of a plain `flutter build web` (dart2js). **skwasm** pairs with the dart2wasm output of `flutter build web --wasm`; it talks to the Wasm-compiled app directly and can render on a web worker when the page is cross-origin isolated. A `--wasm` build also contains a JS + CanvasKit build, and the `flutter.js` loader picks one at page load: skwasm when the browser supports WasmGC and WebGL and its engine is allowed, otherwise the JS fallback. The old DOM-based **HTML renderer** was deprecated and removed, and the `--web-renderer` flag was deleted in Flutter 3.29.

go deeper

for a junior

Recall the two renderers, CanvasKit for the JavaScript build and skwasm for the --wasm build, and that the HTML renderer is gone.

for a middle

Explain how the loader chooses between the two builds at page load and which browser features skwasm needs.

for a senior

Show you know what each choice costs in download size, threading and browser coverage, and how to confirm which build users actually run.

for a principal

Relate the canvas-based rendering model to product decisions: which surfaces suit Flutter web and which need document-style HTML.

## What a web renderer is in Flutter On Android and iOS the Flutter engine paints every pixel with its own renderer. On the web the engine does the same thing inside a browser tab: the framework builds its layer tree exactly as on mobile, and a **web renderer** turns that tree into pixels on a `<canvas>`. Flutter does not translate widgets into HTML elements such as `<div>` or `<button>`. In Flutter 3.47 both web renderers are builds of **Skia**, Flutter's long-standing 2D graphics library, compiled to WebAssembly. **Impeller**, the renderer on iOS, on Android API 29+ and, since 3.47, on desktop, is not used on the web. ## The two renderers | | CanvasKit | skwasm | |---|---|---| | Paired with | JavaScript output (dart2js) | WebAssembly output (dart2wasm) | | Produced by | `flutter build web` | `flutter build web --wasm` | | Browser needs | WebAssembly; WebGL unless forced to CPU-only | **WasmGC**, WebGL, an allowed browser engine | | Threads | main thread | can render on a web worker when the page is cross-origin isolated | | Download | `canvaskit.wasm`, variant chosen by `canvasKitVariant` | `skwasm.wasm`, from the same engine-files location | Key points: - **CanvasKit** is the renderer of the default JavaScript build. Its runtime setting `canvasKitVariant` defaults to `auto`; `chromium` downloads a smaller variant for Chromium-only audiences. - **skwasm** exists because a Wasm-compiled Dart app can call a Wasm renderer without going through JavaScript. It is selected only when the app itself was compiled with dart2wasm. - By default the tool points the loader at a Google-hosted CDN for these engine files; `--no-web-resources-cdn` serves them from your own build output. ## How an app ends up on one or the other 1. **Build time.** `flutter build web` produces only the JavaScript build, so the app always runs on CanvasKit. `flutter build web --wasm` produces two builds: dart2wasm with skwasm, plus a dart2js build with CanvasKit as a fallback. The tool refuses to combine `--wasm` with a non-default renderer. 2. **Page load.** The generated `flutter_bootstrap.js` calls `_flutter.loader.load()`, which walks the builds in order and takes the first one the browser can run. skwasm is skipped if the browser lacks WasmGC or WebGL, or if its engine is not on the Wasm allow list, which by default enables only Chromium-based browsers. 3. **Result.** The same deployment can run skwasm in desktop Chrome and CanvasKit in Safari or Firefox. Code can check which compiler produced the running app with `const bool.fromEnvironment('dart.tool.dart2wasm')`. ## The removed HTML renderer Older Flutter versions offered an **HTML renderer** that mapped drawing onto DOM elements and CSS, chosen with `--web-renderer html`. It rendered inconsistently with mobile and was deprecated; Flutter 3.29 removed the `--web-renderer` flag from the tool and the renderer from the docs. Answers that recommend it for smaller downloads or better text describe a Flutter that no longer exists. ## Controlling the choice - `flutter build web --wasm` is the only switch most teams need; the renderer follows the compile target. - The loader's `config` in `flutter_bootstrap.js` accepts a `renderer` value, which makes the loader skip builds for any other renderer, and CanvasKit settings such as `canvasKitVariant`, `canvasKitBaseUrl` and `canvasKitForceCpuOnly`. - If you write a custom `onEntrypointLoaded`, pass that configuration to `initializeEngine()` yourself; the loader forwards it automatically only when you let it start the app. ## Why interviewers ask - It checks you know Flutter web is canvas-based, which explains its SEO, text-selection and accessibility behaviour. - It checks you know the renderer is a consequence of the **compile target**, not a separate switch. - It flushes out stale knowledge: "HTML for mobile browsers, CanvasKit for desktop" was the old auto mode, not the current one.

  • A Flutter team built with --wasm, yet Safari users get CanvasKit. Is the deployment broken?
    No. The `flutter.js` loader tries the dart2wasm + skwasm build first and falls back to the dart2js + CanvasKit build when the browser lacks WasmGC or WebGL or its engine is not on the Wasm allow list, which by default enables only Chromium-based browsers. Serving both builds is the intended behaviour.
  • How can a Flutter web app tell at runtime whether it was compiled to Wasm?
    Read the compile-time constant `const bool.fromEnvironment('dart.tool.dart2wasm')`, which dart2wasm sets. The loader has already chosen a build before Dart code runs, so this answers which build the page actually loaded.

saying these in an interview costs you the question

  • Flutter web turns widgets into HTML elements like div and button.
  • The HTML renderer is still the default on mobile browsers.
  • Impeller renders Flutter web apps in 3.47.
  • You pick skwasm with --web-renderer skwasm.
  • A --wasm build fails to start in browsers without WasmGC.
open as a page

In a Flutter desktop app, where do you set the initial window size and title, and why is that not in Dart code?

level: juniorimportance: should knowfreq 38%

basics

~20 s

Each desktop target has a generated native runner that creates the OS window before Dart runs: windows/runner/main.cpp, linux/runner/my_application.cc and the macOS MainMenu.xib window. Size and title are set there; the Dart windowing API is still experimental.

open as a page

Why does a sandboxed Flutter macOS app fail network calls with Operation not permitted, and which entitlement files must you edit?

level: middleimportance: should knowfreq 34%

basics

~10 s

Flutter macOS apps are sandboxed by default, and outgoing connections need com.apple.security.network.client, which the template omits. Add it to both macos/Runner/DebugProfile.entitlements and Release.entitlements, keeping them in step.

open as a page

In a Flutter 3.47 desktop app, why do Material buttons show an arrow cursor, and how do you give a custom row a hand cursor?

level: middleimportance: should knowfreq 30%

basics

~20 s

Since Flutter 3.41, Material buttons and InkWell use WidgetStateMouseCursor.adaptiveClickable: a hand on the web, the basic arrow on desktop. For a custom row, wrap it in MouseRegion with cursor: SystemMouseCursors.click and track hover in onEnter and onExit.

open as a page

In a Flutter desktop app, how do you open a context menu at the pointer when the user right-clicks an item?

level: middleimportance: should knowfreq 26%

basics

~10 s

Wrap the item in a MenuAnchor with menuChildren, catch the right-click with GestureDetector.onSecondaryTapUp, and call MenuController.open(position: details.localPosition), which places the menu at that point in the anchor's coordinates.

open as a page

In a Flutter web app, how do you embed a real HTML element such as an iframe, and what does it cost?

level: middleimportance: should knowfreq 33%

basics

~20 s

HtmlElementView reserves layout space for a DOM element: fromTagName creates it and onElementCreated configures it through package:web. The element receives pointer events Flutter never sees, and every one splits the canvas into overlays that cost memory and speed.

open as a page

Why do Flutter web URLs contain #/ by default, and what does switching to usePathUrlStrategy require in the app and on the server?

level: middleimportance: should knowfreq 42%

basics

~20 s

Flutter web defaults to the hash URL strategy, keeping routes after # so any static server works. usePathUrlStrategy from flutter_web_plugins, called before runApp, gives clean paths, but the server must rewrite unknown paths to index.html.

open as a page

What does Flutter's flutter build web --wasm produce, when do browsers fall back to JavaScript, and why do COOP and COEP headers matter?

level: middleimportance: should knowfreq 40%

basics

~20 s

flutter build web --wasm emits a dart2wasm build on skwasm plus a dart2js build on CanvasKit. Browsers without WasmGC, WebGL or default Wasm enablement get the JavaScript build. Without COOP and COEP headers skwasm still runs, but single-threaded.

open as a page

Taking a Flutter invoice tool for small shops from mobile to Windows and macOS, what breaks beyond the UI, and how do you package it for each OS?

level: seniorimportance: should knowfreq 36%

basics

~20 s

Plugins without Windows or macOS implementations throw MissingPluginException, and macOS needs sandbox entitlements. flutter build windows produces a folder of exe, DLLs and data to ship with the VC++ runtime or package as MSIX; macOS ships as a signed archive.

open as a page

Why does a Flutter web app index poorly in search engines and load slower on first visit than a plain HTML page, and what can you do?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Flutter web paints into a canvas, so crawlers find little document HTML, and the semantics DOM is off by default. First load must fetch the renderer's Wasm and the compiled app. Keep searchable content in HTML and use Flutter for app-like surfaces.

open as a page

A delivery service has a Flutter courier app and needs an admin dashboard and public order-tracking pages; which would you build with Flutter web, and which as a separate web app?

level: principalimportance: should knowfreq 28%

basics

~20 s

The admin dashboard suits Flutter web: logged-in, app-like, used on desktop, able to share Dart code with the courier app. Public tracking pages need fast first paint, link previews and search, so they fit a lightweight HTML page.

open as a page

What does Flutter's PlatformMenuBar do on macOS, and why does a Flutter app on Windows or Linux need a different menu?

level: middleimportance: nice to knowfreq 18%

basics

~20 s

PlatformMenuBar describes PlatformMenu and PlatformMenuItem entries that macOS renders in its native menu bar; Flutter only supports that out of the box on macOS. Windows and Linux need an in-window menu such as Material's MenuBar.

open as a page