-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Document how images work on the web #5061
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
164 changes: 164 additions & 0 deletions
164
src/docs/development/platform-integration/web-images.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| --- | ||
| title: Displaying images on the web | ||
| short-title: Web Images | ||
| description: Learn how to load and display images on the web. | ||
| --- | ||
|
|
||
| The web supports the standard [Image][1] widget 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. | ||
|
|
||
| # 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][1] widget as well as the low-level | ||
| [dart:ui/Image][11] 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. | ||
|
|
||
| ## Images on the web | ||
|
|
||
| The web offers several methods for displaying images. Below are some of the | ||
| common ones: | ||
|
|
||
| - The built-in [`<img>`][2] and [`<picture>`][3] HTML elements. | ||
| - The [drawImage][4] method on the [`<canvas>`][5] 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 other HTML elements, and they automatically take | ||
| advantage of browser caching, and built-in image optimization and memory | ||
| management. They allow you to safely display images from arbitrary sources | ||
| (more on than in the CORS section below). `drawImage` is great when the image | ||
| must fit within other content rendered using the `<canvas>` element. You also | ||
| gain control over image sizing, and, when the CORS policy allows it, read the | ||
| pixels of the image back for further processing. Finally, WebGL gives you the | ||
| highest degree of control over the image. Not only can you read the pixels and | ||
| apply custom image algorithms, but you can also use GLSL for | ||
| hardware-acceleration. | ||
|
|
||
| ## Cross-Origin Resource Sharing (CORS) | ||
|
|
||
| [CORS][6] 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][21] or | ||
| [fetch][22]. This is to prevent 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. | ||
|
|
||
| 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. | ||
|
|
||
| ## Flutter renderers on the web | ||
|
|
||
| Flutter offers a choice of two renderers on the web: | ||
|
|
||
| - **HTML**: this renderer uses a combination of HTML, CSS, Canvas 2D, and SVG | ||
| to render UI. It uses the `<img>` element to render images. | ||
| - **CanvasKit**: this renderer uses WebGL to render UI, and therefore requires | ||
| access to the pixels of the image. | ||
|
|
||
| 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][7]. | ||
| - No support for [OffsetLayer.toImage][8] and [Scene.toImage][10]. | ||
| - No access to frame data in animated images ([Codec.getNextFrame][9], | ||
| `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. | ||
|
|
||
| # Solutions | ||
|
|
||
| ## In-memory, asset, and same-origin network images | ||
|
|
||
| If the app has the bytes of the encoded image in memory, provided as an | ||
| [asset][12], or stored on the same server that serves the application (a.k.a. | ||
| same-origin), no extra effort is necessary. The image can be displayed using | ||
| the [Image.memory][13], [Image.asset][14], and [Image.network][15] in both HTML | ||
| and CanvasKit modes. | ||
|
|
||
| ## Cross-origin images | ||
|
|
||
| The HTML renderer can load cross-origin images without extra configuration. | ||
|
|
||
| CanvasKit requires that the app get the bytes of the encoded image. There are | ||
| several ways to do this, discussed below. | ||
|
|
||
| ### Host your images in a CORS-enabled CDN. | ||
|
|
||
| Typically, content delivery networks (CDN) can be configured to customize what | ||
| domains are allowed to access your content. For example, Firebase site hosting | ||
| allows [specifying a custom][16] `Access-Control-Allow-Origin` header in the | ||
| `firebase.json` file. | ||
|
|
||
| ### Lack control over the image server? Use a CORS proxy. | ||
|
|
||
| If the image server cannot be configured to allow CORS requests from your | ||
| application, you may still be able to load images by proxying the requests | ||
| through another server. This will require that the intermediate server has | ||
| sufficient access to load the images. | ||
|
|
||
| This method can be used in situations when the original image server serves | ||
| images publicly, but is not configured with the correct CORS headers. | ||
|
|
||
| Examples: | ||
|
|
||
| - Using [CloudFlare Workers][18]. | ||
| - Using [Firebase Functions][19]. | ||
|
|
||
| ### Use <img> in a platform view. | ||
|
|
||
| Flutter supports embedding HTML inside the app using [HtmlElementView][17]. Use | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filed an issue to add sample code for this: #5063 |
||
| 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 the | ||
| section "Flutter renderers on the web" above. | ||
|
|
||
| [As of today][20], using too many HTML elements with the CanvasKit renderer may | ||
| hurt performance. If images interleave non-image content Flutter will need to | ||
| create extra WebGL contexts between the `<img>` elements. If your application | ||
| needs to display a lot of images on the same screen all at once, consider using | ||
| the HTML renderer instead of CanvasKit. | ||
|
|
||
|
|
||
| [1]: https://api.flutter.dev/flutter/widgets/Image-class.html | ||
| [2]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img | ||
| [3]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture | ||
| [4]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage | ||
| [5]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas | ||
| [6]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS | ||
| [7]: https://api.flutter.dev/flutter/dart-ui/Image/toByteData.html | ||
| [8]: https://api.flutter.dev/flutter/rendering/OffsetLayer/toImage.html | ||
| [9]: https://api.flutter.dev/flutter/dart-ui/Codec/getNextFrame.html | ||
| [10]: https://api.flutter.dev/flutter/dart-ui/Scene/toImage.html | ||
| [11]: https://api.flutter.dev/flutter/dart-ui/Image-class.html | ||
| [12]: https://flutter.dev/docs/development/ui/assets-and-images | ||
| [13]: https://api.flutter.dev/flutter/widgets/Image/Image.memory.html | ||
| [14]: https://api.flutter.dev/flutter/widgets/Image/Image.asset.html | ||
| [15]: https://api.flutter.dev/flutter/widgets/Image/Image.network.html | ||
| [16]: https://firebase.google.com/docs/hosting/full-config#headers | ||
| [17]: https://api.flutter.dev/flutter/widgets/HtmlElementView-class.html | ||
| [18]: https://developers.cloudflare.com/workers/examples/cors-header-proxy | ||
| [19]: https://github.com/7kfpun/cors-proxy | ||
| [20]: https://github.com/flutter/flutter/issues/71884 | ||
| [21]: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest | ||
| [22]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You must have at least one line of text between levels of headers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.