Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 128 additions & 1 deletion sites/docs/src/content/deployment/web.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ This page helps you prepare a _release_ version
of your app and covers the following topics:

* [Building the app for release](#building-the-app-for-release)
* [Web content hashing and HTTP caching](#web-content-hashing)
* [Deploying to the web](#deploying-to-the-web)
* [Deploying to Firebase Hosting](#deploying-to-firebase-hosting)
* [Handling images on the web](#handling-images-on-the-web)
* [Opting-in to WebAssembly](#opting-in-to-webassembly)
* [Minification](#minification)
* [Mobile browser viewport and SafeArea](#mobile-browser-viewport-and-safearea)

## Building the app for release

Expand Down Expand Up @@ -79,6 +81,91 @@ from public web hosting (for example, using `"ignore": ["**/*.map"]`
in `firebase.json` or stripping them prior to deployment).
:::

### Web content hashing (--web-content-hash) {:#web-content-hashing}
Comment thread
kevmoo marked this conversation as resolved.

:::version-note
Web content hashing (`--web-content-hash`) requires Flutter 3.50 or later.
:::

To enable automatic content-based cache busting across deployments, pass
`--web-content-hash` to `flutter build web`:

```console
flutter build web --wasm --web-content-hash
```

When `--web-content-hash` is enabled, Flutter embeds an 8-character truncated
SHA-256 content hash into compiled entrypoints and all files under
`build/web/assets/**`, and records the hashed paths in `flutter_bootstrap.js`
(`_flutter.buildConfig`):

* **Hashed immutable files (`Cache-Control: public, max-age=31536000, immutable`)**:
* Compiled entrypoints: `main.dart.<hash>.js`, `main.dart.<hash>.wasm`, and
`main.dart.<hash>.mjs` (plus `main.dart.<hash>.js.map` and
`main.dart.<hash>.mjs.map` if `--source-maps` is enabled).
* All files under `assets/**`: including `AssetManifest.<hash>.bin`,
`AssetManifest.bin.<hash>.json`, `FontManifest.<hash>.json`,
`NOTICES.<hash>`, fonts, images, and `shaders/**`.
Comment thread
kevmoo marked this conversation as resolved.
* **Unhashed bootstrap and root files (`Cache-Control: max-age=0, must-revalidate` or `no-cache`)**:
* `index.html`, `flutter_bootstrap.js`, `flutter.js`, `manifest.json`,
`version.json`, `404.html`, and `icons/**`.
* Bundled `canvaskit/**` files when building with `--no-web-resources-cdn`.
* `main.dart.wasm.map` when building with `--source-maps` (the `.wasm`
binary's `sourceMappingURL` custom section references the unhashed
`.wasm.map` filename).

See the [Web FAQ][web-faq-caching] for a complete `firebase.json` example.

:::note
`--web-content-hash` does not yet support deferred imports (`deferred as`) in
either JavaScript (`dart2js`) or WebAssembly (`dart2wasm` /
`--enable-wasm-deferred-loading`) builds.
:::

#### Overlay deployments for active browser tabs

When you deploy a new release (`N`), users with an already-open browser tab
running the previous release (`N-1`) might lazily fetch an asset, font, or
image whose filename carries release `N-1`'s content hash.

To avoid mid-session `404` errors during rollouts, use an **overlay
deployment** strategy: retain at least the previous release's (`N-1`) hashed
`main.dart.*` and `assets/**` files in your CDN or storage bucket alongside
release `N`'s hashed files (or prune old hashes after a 24–72 hour grace
window), and update `index.html` and `flutter_bootstrap.js` atomically last.
Also exclude `/assets/**`, `/canvaskit/**`, `/icons/**`, and `/main.dart.*`
from single-page application (SPA) `/index.html` catch-all rewrites so missing
assets return a genuine `404` instead of `200 text/html`.

#### Custom service workers (`precache_manifest.json`)

When `--web-content-hash` is enabled, `flutter build web` writes a bill of
materials to `build/web/precache_manifest.json`:

```json title="build/web/precache_manifest.json"
{
"version": 1,
"entries": [
{
"url": "flutter_bootstrap.js",
"hash": "a1b2c3d4",
"size": 10432,
"urlHashed": false
},
{
"url": "main.dart.f215ce97.wasm",
"hash": "f215ce97",
"size": 1842104,
"urlHashed": true
}
]
}
```

Custom PWA service workers or [Workbox][] pipelines can consume
`precache_manifest.json` directly (passing `revision: entry.urlHashed ? null : entry.hash`
so URL-hashed files aren't double-cached with query parameters).

## Opting-in to WebAssembly

To compile and optimize your app for WebAssembly,
Expand Down Expand Up @@ -178,6 +265,42 @@ shorter strings (e.g. by renaming `AlignmentGeometryTween` to something like
| profile | No | Yes |
| release | Yes | Yes |

## Mobile browser viewport and SafeArea {:#mobile-browser-viewport-and-safearea}

:::version-note
Support for `viewport-fit=cover` `SafeArea` insets and URL percent-encoding
preservation in browser history requires Flutter 3.50 or later.
:::

On mobile browsers (such as iOS Safari and Android Chrome) with display cutouts,
notches, or home indicators, Flutter web defaults to rendering within the
browser's safe area (`viewport-fit=contain`).

To render edge-to-edge across the entire screen and populate Flutter's
[`SafeArea`][] and [`MediaQuery.paddingOf`][] with the browser's CSS
`env(safe-area-inset-*)` insets, add a `<meta name="viewport">` tag with
`viewport-fit=cover` inside the `<head>` of your `web/index.html` file (included
as a commented-out option in newly created `web/index.html` templates):

```html title="web/index.html"
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
```

:::note
If an existing web app already includes `viewport-fit=cover` in
`web/index.html`, Flutter 3.50 and later honors it instead of stripping it,
which causes the app to render behind device cutouts unless UI controls are
wrapped in a [`SafeArea`][] widget. For migration details, see the
[`viewport-fit=cover` SafeArea breaking change guide][viewport-fit-breaking].
:::

### URL percent-encoding in browser history

In Flutter 3.50 and later, route strings passed to
`SystemNavigator.routeInformationUpdated` preserve percent-encoded sequences
(such as `%2F`, `%26`, `%23`, and `%20`) when written to the browser's URL bar
and history, rather than decoding them via `Uri.decodeComponent`.

## Embedding a Flutter app into an HTML page

See [Embedding Flutter web][].
Expand All @@ -192,5 +315,9 @@ See [Embedding Flutter web][].
[GitHub Pages]: https://pages.github.com/
[give us feedback]: {{site.repo.flutter}}/issues/new?title=%5Bweb%5D:+%3Cdescribe+issue+here%3E&labels=%E2%98%B8+platform-web&body=Describe+your+issue+and+include+the+command+you%27re+running,+flutter_web%20version,+browser+version
[Google Cloud Hosting]: https://cloud.google.com/solutions/web-hosting
[`MediaQuery.paddingOf`]: {{site.api}}/flutter/widgets/MediaQuery/paddingOf.html
[`SafeArea`]: {{site.api}}/flutter/widgets/SafeArea-class.html
[viewport-fit-breaking]: /release/breaking-changes/viewport-fit-cover-safe-area
[Wasm support]: /platform-integration/web/wasm
[Web renderers]: /platform-integration/web/renderers
[web-faq-caching]: /platform-integration/web/faq#how-do-i-configure-my-cache-headers
[Workbox]: https://github.com/GoogleChrome/workbox
35 changes: 0 additions & 35 deletions sites/docs/src/content/platform-integration/web/building.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,39 +84,6 @@ $ flutter run -d chrome --wasm
To learn more about WebAssembly compilation and server requirements,
check out [Support for WebAssembly (Wasm)][Wasm support].

### Disable hot reload in VS Code

To temporarily disable hot reload support from VS Code,
update your [`launch.json` file][] file with
the flag `--no-web-experimental-hot-reload`.

```plaintext
"configurations": [
...
{
"name": "Flutter for web (hot reload disabled)",
"type": "dart",
"request": "launch",
"program": "lib/main.dart",
"args": [
"-d",
"chrome",
"--no-web-experimental-hot-reload",
]
}
]
```

### Disable hot reload from the command line

If you use `flutter run` from the command line,
you can temporarily disable hot reload on the web with the
following command:

```console
flutter run -d chrome --no-web-experimental-hot-reload
```

### Use hot reload in DartPad

Hot reload is also enabled in DartPad with a new "Reload" button.
Expand All @@ -126,7 +93,6 @@ session by selecting a sample app provided by DartPad.

[Hot restart]: /tools/hot-reload
[How to switch channels]: /install/upgrade#switching-flutter-channels
[`launch.json` file]: https://code.visualstudio.com/docs/debugtest/debugging-configuration
[Web Hot Reload issue template]: {{site.repo.dart-sdk}}/issues/new?template=5_web_hot_reload.yml
[GitHub project]: https://github.com/orgs/dart-lang/projects/107/views/1

Expand Down Expand Up @@ -194,5 +160,4 @@ running integration tests in a browser, check out the [Integration testing][] pa
[Setup-web]: /platform-integration/web/setup
[Web FAQ]: /platform-integration/web/faq
[Wasm support]: /platform-integration/web/wasm
[Web renderers]: /platform-integration/web/renderers
[Widget tests]: /testing/overview#widget-tests
66 changes: 55 additions & 11 deletions sites/docs/src/content/platform-integration/web/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,24 @@ to backend services.

### How do I clear the web cache after a deployment and force an app download?

If you wish to defeat these cache headers after each deployment,
a common technique is to append a build ID of some sort to the links
of your static resources, or update the filenames themselves.
For example, `logo.png` might become `logo.v123.png`.
In Flutter 3.50 and later, pass `--web-content-hash` when building your web app:

```console
$ flutter build web --web-content-hash
```

The `--web-content-hash` flag embeds an 8-character SHA-256 content hash into
compiled entrypoint filenames (`main.dart.<hash>.js`, `main.dart.<hash>.wasm`,
`main.dart.<hash>.mjs`), JavaScript source maps (`main.dart.<hash>.js.map`,
`main.dart.<hash>.mjs.map` when `--source-maps` is enabled), and all files under
`build/web/assets/**` (including `AssetManifest.<hash>.bin`,
`FontManifest.<hash>.json`, `NOTICES.<hash>`, and `shaders/**`), updating the
references in `flutter_bootstrap.js` automatically. For full deployment and
overlay rollout guidance, see
[Web content hashing](/deployment/web#web-content-hashing).

If you are not using `--web-content-hash`, you can append a build ID query
parameter or versioned filename to your bootstrap script tag manually:

```html
<!-- Option 1, append build ID as a query parameter in your links -->
Expand All @@ -177,9 +191,6 @@ For example, `logo.png` might become `logo.v123.png`.
<script src="flutter_bootstrap.v123.js" async></script>
```

Flutter doesn't currently support appending build IDs to resources
automatically.

### How do I configure my cache headers?

If you are using Firebase Hosting,
Expand All @@ -189,15 +200,22 @@ However, by default Firebase Hosting sets `Cache-Control: max-age=3600`
on static files, meaning browsers can serve stale `index.html`,
`flutter_bootstrap.js`, or manifest files for up to an hour after a deploy.

To make sure that browsers revalidate your app's files on each load
(receiving fast `304 Not Modified` responses from the CDN edge when unchanged)
and don't cache missing assets (`404.html`),
configure your `firebase.json` file as follows:
If you build with `flutter build web --web-content-hash`, configure
`firebase.json` so that `index.html`, `flutter_bootstrap.js`, and `404.html`
revalidate on every load (`max-age=0, must-revalidate`) while hashed
entrypoints and `assets/**` are cached immutably for one year
(`public, max-age=31536000, immutable`):

```json
{
"hosting": {
"public": "build/web",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**",
"**/*.map"
],
"headers": [
{
"source": "**",
Expand All @@ -208,6 +226,24 @@ configure your `firebase.json` file as follows:
}
]
},
{
"source": "assets/**",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
},
{
"source": "main.dart.*.+(js|wasm|mjs)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
},
{
"source": "404.html",
"headers": [
Expand All @@ -228,6 +264,11 @@ configure your `firebase.json` file as follows:
}
```

If you build without `--web-content-hash`, omit the `immutable` rules for
`assets/**` and `main.dart.*.+(js|wasm|mjs)` so that all files revalidate via
`"source": "**"` and `"source": "404.html"` (`max-age=0, must-revalidate`),
receiving fast `304 Not Modified` responses from the CDN edge when unchanged.

The `/index.html` rewrite exclusion
(`!(/assets/**|/canvaskit/**|/icons/**|/main.dart.*)`) ensures that requests
for missing assets or compiled entry points return a real `404` status rather
Expand All @@ -243,6 +284,9 @@ legacy service workers).
If your application requires offline support or advanced caching,
you need to configure a service worker yourself using standard
web tooling or third-party solutions such as [Workbox][workbox].
When building with `--web-content-hash`, Flutter generates
`build/web/precache_manifest.json` containing `{url, hash, size, urlHashed}`
entries that you can feed into your custom service worker build pipeline.

For more information on building custom service workers,
check out [Using service workers][].
Expand Down
20 changes: 8 additions & 12 deletions sites/docs/src/content/platform-integration/web/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,13 @@ check out the following video.

## How it works

Adding web support to Flutter involved implementing Flutter's
core drawing layer on top of standard browser APIs,
in addition to compiling Dart to JavaScript,
instead of the ARM machine code that
is used for mobile applications.
Using a combination of DOM, Canvas, and WebAssembly,
Flutter can provide a portable, high-quality,
and performant user experience across modern browsers.
We implemented the core drawing layer completely in Dart
and used Dart's optimized JavaScript compiler to compile the
Flutter core and framework along with your application into a single,
minified source file that can be deployed to any web server.
Flutter on the web compiles your Dart application and the Flutter framework to
either JavaScript (using `dartdevc` during development and `dart2js` for
production) or [WebAssembly][] (`dart2wasm` when building with `--wasm`),
instead of the ARM or x64 machine code used on mobile and desktop platforms.
Flutter renders your UI using hardware-accelerated browser graphics APIs
(WebGL and WebGPU) through its CanvasKit and Skwasm renderers, delivering a
portable, consistent, and high-performance experience across modern browsers.

<img src="/assets/images/docs/arch-overview/web-framework-diagram.png"
alt="Flutter architecture for web" >
Expand Down Expand Up @@ -110,3 +105,4 @@ The following resources can help you get started:
[Progressive Web Application]: https://web.dev/progressive-web-apps/
[web FAQ]: /platform-integration/web/faq
[web samples for Flutter]: https://github.com/flutter/samples/#?platform=web
[WebAssembly]: /platform-integration/web/wasm
Loading
Loading