使用 HTML 插槽在 Web 中渲染平台视图
概述
#Flutter 现在会将所有 Web 平台视图渲染到 DOM 的一个固定位置,作为 flt-glass-pane
的直接子元素(无论使用何种渲染后端:html
还是 canvaskit
)。然后,平台视图会通过标准的 HTML 功能 "插入" 到应用程序 DOM 的正确位置。
在此更改之前,Flutter Web 会修改平台视图渲染内容的样式,以将其定位/调整到可用空间。现在情况不再是这样了。用户现在可以决定他们希望如何利用框架分配给其平台视图的空间。
背景
#Flutter 框架会频繁调整其渲染树,以优化每帧最终执行的绘制操作。在 Web 端,这些渲染树的变化通常会导致 DOM 操作。
Flutter Web 过去会将平台视图(HtmlElementView
小部件)直接渲染到 DOM 中对应的位置。
将某些 DOM 元素用作某些 DOM 操作的 "目标" 会导致这些元素丢失其内部状态。实际上,这意味着 iframe
标签会重新加载,video
播放器可能会重启,或者可编辑表单可能会丢失其编辑内容。
Flutter 现在使用 slot 元素 在一个全局应用程序的 Shadow DOM 中渲染平台视图。Slot 元素可以被添加到/移除/移动到 Shadow DOM 的不同位置,而不会影响底层的插槽内容(这些内容在一个固定的位置渲染)。
此更改是为了:
- 稳定 Flutter Web 中平台视图的行为。
- 统一两种渲染后端(
html
和canvaskit
)在 Web 中渲染平台视图的方式。 - 提供一个可预测的 DOM 位置,使开发人员能够可靠地使用 CSS 来设置其平台视图的样式,并使用其他标准的 DOM API,如
querySelector
和getElementById
。
变更说明
#Flutter Web 应用程序现在渲染在一个通用的 Shadow DOM 中,其中 slot 元素 代表平台视图。每个平台视图的实际内容被渲染为该 Shadow DOM 的同级元素。
之前
#...
<flt-glass-pane>
...
<div id="platform-view">Contents</div> <!-- canvaskit -->
<!-- OR -->
<flt-platform-view>
#shadow-root
| <div id="platform-view">Contents</div> <!-- html -->
</flt-platform-view>
...
</flt-glass-pane>
...
之后
#...
<flt-glass-pane>
#shadow-root
| ...
| <flt-platform-view-slot>
| <slot name="platform-view-1" />
| </flt-platform-view-slot>
| ...
<flt-platform-view slot="platform-view-1">
<div id="platform-view">Contents</div>
</flt-platform-view>
...
</flt-glass-pane>
...
在此更改之后,当框架需要移动 DOM 节点时,它会操作 flt-platform-view-slot
,其中仅包含一个 slot
元素。该 slot 将 flt-platform-view
元素中定义的内容 "投射" 到 Shadow DOM 之外。flt-platform-view
元素永远不会成为框架 DOM 操作的目标,从而防止了重新加载问题。
从应用程序的角度来看,此更改是透明的。然而,这被认为是一个破坏性更改,因为一些测试会依赖 Flutter Web 应用程序的内部 DOM 结构,从而导致失败。
迁移指南
#代码
#引擎可能会在控制台打印类似如下的警告信息:
Height of Platform View type: [$viewType] may not be set. Defaulting to `height: 100%`.
Set `style.height` to any appropriate value to stop this message.
或
Width of Platform View type: [$viewType] may not be set. Defaulting to `width: 100%`.
Set `style.width` to any appropriate value to stop this message.
以前,PlatformViewFactory
函数返回的内容由框架进行缩放和定位。现在,Flutter 对 <flt-platform-view-slot>
(内容被投射的插槽的父级)进行缩放和定位。
要停止上述警告,平台视图需要将其根元素的 style.width
和 style.height
设置为任何适当的(非 null)值。
例如,要使根 html.Element
填充框架分配的所有可用空间,请将其 style.width
和 style.height
属性设置为 '100%'
。
ui.platformViewRegistry.registerViewFactory(viewType, (int viewId) {
final html.Element htmlElement = html.DivElement()
// ..other props
..style.width = '100%'
..style.height = '100%';
// ...
return htmlElement;
});
如果使用其他技术来布局平台视图(例如 inset: 0
),则将 width
和 height
设置为 auto
就足以停止警告。
阅读更多关于 CSS width
和 CSS height
的信息。
测试
#在此更改之后,用户的测试代码不需要深入检查应用程序的 Shadow DOM 内容。所有平台视图内容都将作为 flt-glass-pane
的直接子元素放置,并封装在 flt-platform-view
元素中。
避免查看 flt-glass-pane
Shadow DOM 的内部,这被认为是“私有实现细节”,其标记可能会随时更改,恕不另行通知。
(参见下面的相关 PRs,了解上述 "迁移" 的示例)。
时间线
#已登陆版本:2.3.0-16.0.pre
稳定版本:2.5
参考资料
#设计文档
相关问题
相关 PR
- flutter/engine#25747: 引入该功能。
- flutter/flutter#82926: 调整
flutter
测试。 - flutter/plugins#3964: 调整
plugins
代码。 - flutter/packages#364: 调整
packages
代码。