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 created viewId as a parameter, and needs ui_web.platformViewRegistry.getViewById to retrieve the created element (See PlatformViewCreatedCallback).
  • onElementCreated will be called with the created element directly, skipping its viewId (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);
}

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 (from View.of(context)) represents the FlutterView where the web app is currently rendering.
  • viewId (passed to the viewFactory function) represents a unique ID for the HtmlElementView 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 HtmlElementViews (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

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}) 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