在 Web 中使用 HTML slot 渲染平台视图
概述
#Flutter 现在将所有 web 平台视图渲染到 DOM 的一个一致位置,作为 flt-glass-pane
的直接子元素(无论渲染后端是 html
还是 canvaskit
)。然后,平台视图使用标准 HTML 特性被“slot 到”App DOM 中的正确位置。
在此更改之前,Flutter web 会更改平台视图渲染内容的样式,以将其定位/调整大小以适应可用空间。**现在不再是这种情况了。** 用户现在可以决定如何利用框架为其平台视图分配的空间。
背景
#Flutter 框架会频繁调整其渲染树,以优化最终每帧进行的绘制操作。在 web 中,这些渲染树更改通常会导致 DOM 操作。
Flutter web 过去直接将其平台视图(HtmlElementView
组件)渲染到 DOM 的相应位置。
使用某些 DOM 元素作为某些 DOM 操作的“目标”会导致这些元素丢失其内部状态。实际上,这意味着 iframe
标签会重新加载,video
播放器可能会重新启动,或者可编辑表单可能会丢失其编辑内容。
Flutter 现在使用slot 元素在一个单一的、应用范围的shadow root内部渲染平台视图。Slot 元素可以在 Shadow DOM 中添加/删除/移动,而不会影响底层被 slot 的内容(这些内容在一个固定位置渲染)
做出此更改是为了
- 稳定 Flutter web 中平台视图的行为。
- 统一
html
和canvaskit
这两个渲染后端在 web 中渲染平台视图的方式。 - 在 DOM 中提供一个可预测的位置,允许开发者可靠地使用 CSS 来样式化其平台视图,并使用其他标准 DOM API,例如
querySelector
和getElementById
。
变更说明
#Flutter web 应用现在渲染在一个公共的shadow root内部,其中slot 元素代表平台视图。每个平台视图的实际内容作为**该 shadow root 的同级元素**渲染。
之前
#...
<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 root 外部。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>
的大小和位置,它是内容投射到的 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
的信息。
测试
#此更改后,用户的测试代码**不再**需要深入检查 App 的 shadow root 内容。所有平台视图内容都将作为 flt-glass-pane
的直接子元素放置,并封装在 flt-platform-view
元素中。
避免查看 flt-glass-pane
的 shadow root 内部,它被视为**“私有实现细节”**,其标记可能随时更改,恕不另行通知。
(请参阅下面的相关 PR 以获取上述“迁移”的示例)。
时间线
#已在版本中发布:2.3.0-16.0.pre
稳定版本:2.5
参考资料
#设计文档
相关问题
相关 PR
- flutter/engine#25747:引入了该功能。
- flutter/flutter#82926:调整
flutter
测试。 - flutter/plugins#3964:调整
plugins
代码。 - flutter/packages#364:调整
packages
代码。