HtmlElementView class
Embeds an HTML element in the Widget hierarchy in Flutter web.
The embedded HTML is laid out like any other Flutter widget and transformations (like opacity, and clipping) apply to it as well.
The widget fills all available space, the parent of this object must provide bounded layout constraints.
Embedding HTML is a potentially expensive operation and should be avoided
when a Flutter equivalent is possible. (See isVisible
parameter below.)
This widget is useful to integrate native HTML elements to a Flutter web app,
like a <video>
tag, or a <div>
where a Google Map
can be rendered.
This widget only works on Flutter web. To embed web content on other
platforms, consider using the webview_flutter
plugin.
Usage
There's two ways to use the HtmlElementView
widget:
HtmlElementView.fromTagName
The HtmlElementView.fromTagName constructor creates the HTML element
specified by tagName
, and passes it to the onElementCreated
callback
where it can be customized:
// In a `build` method...
HtmlElementView.fromTagName(
tagName: 'div',
onElementCreated: myOnElementCreated,
);
The example creates a <div>
element, then calls the onElementCreated
callback with the created <div>
, so it can be customized before it is
attached to the DOM.
(See more details about onElementCreated
in the Lifecycle section below.)
Using the PlatformViewRegistry
The primitives used to implement HtmlElementView.fromTagName are available
for general use through dart:ui_web
's platformViewRegistry
.
Creating an HtmlElementView
through these primitives is a two step process:
1. registerViewFactory
First, a viewFactory
function needs to be registered for a given viewType
.
Flutter web will call this factory function to create the element
that will
be attached later:
import 'dart:ui_web' as ui_web;
import 'package:web/web.dart' as web;
void registerRedDivFactory() {
ui_web.platformViewRegistry.registerViewFactory(
'my-view-type',
(int viewId, {Object? params}) {
// Create and return an HTML Element from here
final web.HTMLDivElement myDiv = web.HTMLDivElement()
..id = 'some_id_$viewId'
..style.backgroundColor = 'red'
..style.width = '100%'
..style.height = '100%';
return myDiv;
},
);
}
registerViewFactory
must be called outside of build
methods, so the
registered function is available when build
happens.
See the different types of functions that can be used as viewFactory
:
2. HtmlElementView
widget
Once a factory is registered, an HtmlElementView
widget of viewType
can
be added to the widget tree, like so:
// In a `build` method...
const HtmlElementView(
viewType: 'my-view-type',
onPlatformViewCreated: myOnPlatformViewCreated,
creationParams: <String, Object?>{
'key': 'someValue',
},
);
viewType must match the value used to registerViewFactory
before.
creationParams (optional) will be passed to your viewFactory
function,
if it accepts them.
onPlatformViewCreated will be called with the viewId
of the platform
view (element
) created by the viewFactory
, before it gets attached to
the DOM.
The viewId
can be used to retrieve the created element
(The same one
passed to onElementCreated
in HtmlElementView.fromTagName) with the
ui_web.platformViewRegistry.
getViewById
method.
(See more details about onPlatformViewCreated
in the Lifecycle section
below.)
Lifecycle
HtmlElementView
widgets behave like any other Flutter stateless widget, but
with an additional lifecycle method: onPlatformViewCreated
/ onElementCreated
(depending on the constructor, see Usage above).
The difference between the two callbacks is the parameter they receive:
onPlatformViewCreated
will be called with the createdviewId
as a parameter, and needsui_web.platformViewRegistry.getViewById
to retrieve the created element (See PlatformViewCreatedCallback).onElementCreated
will be called with the createdelement
directly, skipping itsviewId
(See ElementCreatedCallback).
Both callbacks are called after the HTML element
has been created, but
before it's attached to the DOM.
HTML Lifecycle
The Browser DOM APIs have additional HTML lifecycle callbacks for the root
element
of an HtmlElementView
.
Element Attached To The DOM
It is common for JS code to locate the DOM elements they need with a
selector, rather than accepting said DOM elements directly. In those cases,
the element
must be attached to the DOM for the selector to work.
The example below demonstrates how to create an onElementAttached
function
that gets called when the root element
is attached to the DOM using a
ResizeObserver
through package:web
from the onElementCreated
lifecycle
method:
import 'dart:js_interop';
import 'package:web/web.dart' as web;
// Called after `element` is attached to the DOM.
void onElementAttached(web.HTMLDivElement element) {
final web.Element? located = web.document.querySelector('#someIdThatICanFindLater');
assert(located == element, 'Wrong `element` located!');
// Do things with `element` or `located`, or call your code now...
element.style.backgroundColor = 'green';
}
void onElementCreated(Object element) {
element as web.HTMLDivElement;
element.style.backgroundColor = 'red';
element.id = 'someIdThatICanFindLater';
// Create the observer
final web.ResizeObserver observer = web.ResizeObserver((
JSArray<web.ResizeObserverEntry> entries,
web.ResizeObserver observer,
) {
if (element.isConnected) {
// The observer is done, disconnect it.
observer.disconnect();
// Call our callback.
onElementAttached(element);
}
}.toJS);
// Connect the observer.
observer.observe(element);
}
- Read more about
ResizeObserver
in the MDN.
Other Observers
The example above uses a ResizeObserver
because it can be applied directly
to the element
that is about to be attached. Another observer that could
be used for this (with a little bit more code) would be a
MutationObserver
.
The MutationObserver
requires the parent element in which the HtmlElementView
is going to be inserted. A safe way to retrieve a parent element for the
platform view is to retrieve the hostElement
of the FlutterView where the
HtmlElementView
is being rendered.
The hostElement
of the current FlutterView can be retrieved through:
import 'dart:js_interop';
import 'dart:ui_web' as ui_web;
import 'package:flutter/widgets.dart';
void useHostElement(BuildContext context) {
final int flutterViewId = View.of(context).viewId;
final JSAny? hostElement = ui_web.views.getHostElement(flutterViewId);
// Use `package:web` with `hostElement`...
}
Important: FlutterView.viewId
and the viewId
parameter passed to
the viewFactory
identify different objects:
flutterViewId
(fromView.of(context)
) represents the FlutterView where the web app is currently rendering.viewId
(passed to theviewFactory
function) represents a unique ID for theHtmlElementView
instance that is being attached to the app.
Read more about FlutterView on Flutter's API docs:
Pointer events
In order for the HtmlElementView
contents to be interactive, they're allowed
to handle pointer-events
. This may result in Flutter missing some events
because they've been handled by the HtmlElementView
, and not seen by
Flutter.
package:pointer_interceptor
may help in some cases where Flutter content needs to be overlaid on top of
an HtmlElementView
. Alternatively, the pointer-events: none
property can
be set onElementCreated
; but that will prevent ALL interactions with
the underlying HTML content.
If the HtmlElementView
is an <iframe>
element, Flutter will not receive
pointer events that land in the <iframe>
(click/tap, drag, drop, etc.)
In those cases, the HtmlElementView
will seem like it's swallowing
the events and not participating in Flutter's gesture detection.
isVisible
parameter
Rendering custom HTML content (from HtmlElementView
) in between canvas
pixels means that the Flutter web engine needs to split the canvas drawing
into elements drawn behind the HTML content, and those drawn above it.
In the Flutter web engine, each of these splits of the canvas to sandwich HTML content in between is referred to as an overlay.
Each overlay present in a scene has implications both in memory and execution performance, and it is best to minimize their amount; browsers support a limited number of overlays on a single scene at a given time.
HtmlElementView
objects have an isVisible
property that can be passed
through registerViewFactory
, or fromTagName
. isVisible
refers
to whether the HtmlElementView
will paint pixels on the screen or not.
Correctly defining this value helps the Flutter web rendering engine optimize the amount of overlays it'll need to render a particular scene.
In general, isVisible
should be left to its default value of true
, but
in some HtmlElementView
s (like the pointer_interceptor
or Link
widget),
it can be set to false
, so the engine doesn't waste an overlay to render
Flutter content on top of views that don't paint any pixels.
- Inheritance
-
- Object
- DiagnosticableTree
- Widget
- StatelessWidget
- HtmlElementView
Constructors
- HtmlElementView({Key? key, required String viewType, PlatformViewCreatedCallback? onPlatformViewCreated, Object? creationParams})
-
Creates a platform view for Flutter web.
const
- HtmlElementView.fromTagName({Key? key, required String tagName, bool isVisible = true, ElementCreatedCallback? onElementCreated})
-
Creates a platform view that creates a DOM element specified by
tagName
.factory
Properties
- creationParams → Object?
-
Passed as the 2nd argument (i.e.
params
) of the registered view factory.final - hashCode → int
-
The hash code for this object.
no setterinherited
- key → Key?
-
Controls how one widget replaces another widget in the tree.
finalinherited
- onPlatformViewCreated → PlatformViewCreatedCallback?
-
Callback to invoke after the platform view has been created.
final
- runtimeType → Type
-
A representation of the runtime type of the object.
no setterinherited
- viewType → String
-
The unique identifier for the HTML view type to be embedded by this widget.
final
Methods
-
build(
BuildContext context) → Widget -
Describes the part of the user interface represented by this widget.
override
-
createElement(
) → StatelessElement -
Creates a StatelessElement to manage this widget's location in the tree.
inherited
-
debugDescribeChildren(
) → List< DiagnosticsNode> -
Returns a list of DiagnosticsNode objects describing this node's
children.
inherited
-
debugFillProperties(
DiagnosticPropertiesBuilder properties) → void -
Add additional properties associated with the node.
inherited
-
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
toDiagnosticsNode(
{String? name, DiagnosticsTreeStyle? style}) → DiagnosticsNode -
Returns a debug representation of the object that is used by debugging
tools and by DiagnosticsNode.toStringDeep.
inherited
-
toString(
{DiagnosticLevel minLevel = DiagnosticLevel.info}) → String -
A string representation of this object.
inherited
-
toStringDeep(
{String prefixLineOne = '', String? prefixOtherLines, DiagnosticLevel minLevel = DiagnosticLevel.debug, int wrapWidth = 65}) → String -
Returns a string representation of this node and its descendants.
inherited
-
toStringShallow(
{String joiner = ', ', DiagnosticLevel minLevel = DiagnosticLevel.debug}) → String -
Returns a one-line detailed description of the object.
inherited
-
toStringShort(
) → String -
A short, textual description of this widget.
inherited
Operators
-
operator ==(
Object other) → bool -
The equality operator.
inherited