概述

#

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 中平台视图的行为。
  • 统一 htmlcanvaskit 这两个渲染后端在 web 中渲染平台视图的方式。
  • 在 DOM 中提供一个可预测的位置,允许开发者可靠地使用 CSS 来样式化其平台视图,并使用其他标准 DOM API,例如 querySelectorgetElementById

变更说明

#

Flutter web 应用现在渲染在一个公共的shadow root内部,其中slot 元素代表平台视图。每个平台视图的实际内容作为**该 shadow root 的同级元素**渲染。

之前

#
html
...

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

...

之后

#
html
...

<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 做出假设,并因此而失效。

迁移指南

#

代码

#

引擎可能会向控制台打印类似于以下内容的警告消息:

bash
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.

bash
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.widthstyle.height 设置为任何适当的(非 null)值。

例如,要使根 html.Element 填充框架分配的所有可用空间,请将其 style.widthstyle.height 属性设置为 '100%'

dart
ui.platformViewRegistry.registerViewFactory(viewType, (int viewId) {
  final html.Element htmlElement = html.DivElement()
    // ..other props
    ..style.width = '100%'
    ..style.height = '100%';
  // ...
  return htmlElement;
});

如果使用其他技术来布局平台视图(例如 inset: 0),则将 widthheight 的值设置为 auto 就足以停止警告。

了解更多关于 CSS widthCSS 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