Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/flutter/website into ew-fwe…
Browse files Browse the repository at this point in the history
…-sidebar
  • Loading branch information
ericwindmill committed Sep 24, 2024
2 parents 5fc5228 + 21ae949 commit 15fae76
Showing 1 changed file with 47 additions and 96 deletions.
143 changes: 47 additions & 96 deletions src/content/platform-integration/web/web-images.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ short-title: Web images
description: Learn how to load and display images on the web.
---

The web supports the standard [`Image`][] widget to display images.
The web supports the standard [`Image`][] widget and the more
advanced [`dart:ui/Image`][] class (where more fine-grained control
is needed to display images).
However, because web browsers are built to run untrusted code safely,
there are certain limitations in what you can do with images compared
to mobile and desktop platforms. This page explains these limitations
and offers ways to work around them.

[`Image`]: {{site.api}}/flutter/widgets/Image-class.html
[`dart:ui/Image`]: {{site.api}}/flutter/dart-ui/Image-class.html

:::note
For information on how to optimize web loading speed,
Expand All @@ -20,32 +23,13 @@ check out the (free) article on Medium,
[article]: {{site.flutter-medium}}/best-practices-for-optimizing-flutter-web-loading-speed-7cc0df14ce5c
:::


## Background

This section summarizes the technologies available
across Flutter and the web,
on which the solutions below are based on.

### Images in Flutter

Flutter offers the [`Image`][] widget as well as the low-level
[`dart:ui/Image`][] class for rendering images.
The `Image` widget has enough functionality for most use-cases.
The `dart:ui/Image` class can be used in
advanced situations where fine-grained control
of the image is needed.

[`dart:ui/Image`]: {{site.api}}/flutter/dart-ui/Image-class.html

### Images on the web

The web offers several methods for displaying images.
Below are some of the common ones:
The web offers several methods for displaying images:

- The built-in [`<img>`][] and [`<picture>`][] HTML elements.
- The [`drawImage`][] method on the [`<canvas>`][] element.
- Custom image codec that renders to a WebGL canvas.
- The built-in [`<img>`][] and [`<picture>`][] HTML elements
- The [`drawImage`][] method on the [`<canvas>`][] element
- Custom image codec that renders to a WebGL canvas

Each option has its own benefits and drawbacks.
For example, the built-in elements fit nicely among
Expand All @@ -69,96 +53,65 @@ hardware-acceleration.
[`drawImage`]: https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/drawImage
[`<canvas>`]: https://developer.mozilla.org/docs/Web/HTML/Element/canvas

### Cross-Origin Resource Sharing (CORS)
## Cross-Origin Resource Sharing (CORS)

[CORS][] is a mechanism that browsers use to control
how one site accesses the resources of another site.
It is designed such that, by default, one web-site
is not allowed to make HTTP requests to another site
using [XHR][] or [`fetch`][].
This prevents scripts on another site from acting on behalf
of the user and from gaining access to another
[CORS][] is a mechanism that browsers use to control how
one site accesses the resources of another site. It is
designed such that, by default, one web-site is not
allowed to make HTTP requests to another site using
[XHR][] or [`fetch`][].
This prevents scripts on another site from acting on
behalf of the user and from gaining access to another
site's resources without permission.

When using `<img>`, `<picture>`, or `<canvas>`,
the browser automatically blocks access to pixels
when it knows that an image is coming from another site
and the CORS policy disallows access to data.
On the web, Flutter renders apps using the CanvasKit
or skwasm (when using Wasm) renderers. These both rely
on WebGL. WebGL requires access to the raw image data
(bytes) in order to be able to render the image.
Therefore, images must only come from servers that
have a CORS policy configured to work with the domain
that serves your application.

:::note
For more information about web renderers, see
[Web renderers][].
:::

:::version-note
The HTML web renderer has fewer CORS limitations, but does
not support the full feature set as the WebGL renderers do.

WebGL requires access to the image data in order
to be able to render the image. Therefore,
images to be rendered using WebGL must only come from servers
that have a CORS policy configured to work with
the domain that serves your application.
The HTML renderer will be deprecated and removed in a
future Flutter version. Therefore, the HTML method to render
images using `<img>` & `<picture>` elements will no longer be
applicable to Flutter (without a platform view).
:::

[CORS]: https://developer.mozilla.org/docs/Web/HTTP/CORS
[XHR]: https://developer.mozilla.org/docs/Web/API/XMLHttpRequest
[`fetch`]: https://developer.mozilla.org/docs/Web/API/Fetch_API/Using_Fetch

### Flutter renderers on the web

Flutter offers a choice of two renderers on the web:

**CanvasKit**
: Uses WebGL to render UI, and therefore
requires access to the pixels of the image.

**HTML**
: Uses a combination of HTML, CSS, Canvas 2D, and SVG to render UI.
It uses the `<img>` element to render images.

Because the HTML renderer uses the `<img>`
element it can display images from
arbitrary sources. However,
this places the following limitations on what you
can do with them:

* Limited support for [`Image.toByteData`][].
* No support for [`OffsetLayer.toImage`][] and
[`Scene.toImage`][].
* No access to frame data in animated images
([`Codec.getNextFrame`][],
`frameCount` is always 1, `repetitionCount` is always 0).
* No support for `ImageShader`.
* Limited support for shader effects that can be applied to images.
* No control over image memory (`Image.dispose` has no effect).
The memory is managed by the browser behind-the-scenes.

The CanvasKit renderer implements Flutter's image API fully.
However, it requires access to image pixels to do so,
and is therefore subject to the CORS policy.

[`Image.toByteData`]: {{site.api}}/flutter/dart-ui/Image/toByteData.html
[`OffsetLayer.toImage`]: {{site.api}}/flutter/rendering/OffsetLayer/toImage.html
[`Scene.toImage`]: {{site.api}}/flutter/dart-ui/Scene/toImage.html
[`Codec.getNextFrame`]: {{site.api}}/flutter/dart-ui/Codec/getNextFrame.html
[Web renderers]: /platform-integration/web/renderers

## Solutions

There are multiple solutions to workaround CORS restrictions
in Flutter.

### In-memory, asset, and same-origin network images

If the app has the bytes of the encoded image in memory,
provided as an [asset][], or stored on the
same server that serves the application
(also known as _same-origin_), no extra effort is necessary.
The image can be displayed using
[`Image.memory`][], [`Image.asset`][], and [`Image.network`][]
in both HTML and CanvasKit modes.
[`Image.memory`][], [`Image.asset`][], or [`Image.network`][].

[asset]: /ui/assets/assets-and-images
[`Image.memory`]: {{site.api}}/flutter/widgets/Image/Image.memory.html
[`Image.asset`]: {{site.api}}/flutter/widgets/Image/Image.asset.html
[`Image.network`]: {{site.api}}/flutter/widgets/Image/Image.network.html

### Cross-origin images

The HTML renderer can load cross-origin images
without extra configuration.

CanvasKit requires that the app gets the bytes of the encoded image.
There are several ways to do this, discussed below.

#### Host your images in a CORS-enabled CDN.
### Host images in a CORS-enabled CDN

Typically, content delivery networks (CDN)
can be configured to customize what domains
Expand All @@ -169,7 +122,7 @@ header in the `firebase.json` file.

[custom-header]: {{site.firebase}}/docs/hosting/full-config#headers

#### Lack control over the image server? Use a CORS proxy.
### Use a CORS proxy if you have no control over the origin server

If the image server cannot be configured to allow CORS
requests from your application,
Expand All @@ -189,13 +142,11 @@ Examples:
[CloudFlare Workers]: https://developers.cloudflare.com/workers/examples/cors-header-proxy
[Firebase Functions]: {{site.github}}/7kfpun/cors-proxy

#### Use `<img>` in a platform view.
### Use a HTML platform view

Flutter supports embedding HTML inside the app using
If none of the other solutions work for your app, Flutter
supports embedding raw HTML inside the app using
[`HtmlElementView`][]. Use it to create an `<img>`
element to render the image from another domain.
However, do keep in mind that this comes with the
limitations explained in [Flutter renderers on the web][].

[`HtmlElementView`]: {{site.api}}/flutter/widgets/HtmlElementView-class.html
[Flutter renderers on the web]: #flutter-renderers-on-the-web

0 comments on commit 15fae76

Please sign in to comment.