Hummingbird: 在Web上运行Flutter应用

简介: 今天,我们在 Flutter Live 上宣布了一个消息:我们正尝试在 Web 上运行 Flutter。 这篇文章描述了我们应对挑战的方式,以及该技术的当前状态。 在文末,我们附上了协同工作和嵌入等问题的答案。

image.png

原文作者:Yegor Jbanov

译者:UC 国际研发 Jothy


今天,我们在 Flutter Live 上宣布了一个消息:我们正尝试在 Web 上运行 Flutter。 这篇文章描述了我们应对挑战的方式,以及该技术的当前状态。 在文末,我们附上了协同工作和嵌入等问题的答案。

image.png

让我们快速回顾一下 Flutter 的架构。 Flutter 是一个多层系统,这样高的层更易用,用很少的代码就能表达很多,而较低的层能提供更多的控制,代价是必须处理一些复杂性。 当较高层不能满足开发者的需求时,它们可以降到较低层。 开发者可以访问 Flutter Engine 之上的所有层。

image.png

Flutter 的 Mobile 架构

在 Flutter 中,Flutter Engine 作为最低级别的库 dart:ui 暴露。它不关心 组件,物理实现,动画或布局(文本布局除外)。它所关心的是如何将图片组合到屏幕上,渲染变成像素。在 dart:ui 上直接编写应用是很困难的。这正是我们创建更高层的原因。

dart:ui 之上的一切是我们所谓的“框架”。它下面的一切都是“引擎”。该框架完全使用 Dart 语言编写。大多数引擎都是用 C++ 编写的,特定于 Android 的部分用 Java 编写,而 iOS 特定的部分用 Objective-C 编写。 dart:ui 中的一些基本类和函数是用 Dart 编写的,主要用作 Dart 和 C++ 之间的桥梁。

Flutter 还提供插件系统。插件使用指定语言编写,可以直接访问移动生态系统日积月累的 OEM 库和第三方库。你可以使用 Java 或 Kotlin 为 Android 创建插件。 iOS 插件开发是使用 Objective-C 或 Swift。

Hello, The Web

Web 平台已经发展了数十年,包含了许多技术和规范。 有一些涵盖性术语用于描述大量相关功能:HTML,CSS,SVG,JavaScript,WebGL。 为了在 Web 上运行 Flutter,我们需要:

  • 编译 Dart 代码:Flutter 是用 Dart 编写的,我们需要在 Web 上运行 Dart。
  • 选择要在 Web 上运行的 Flutter 子集:在 Web 上运行所有 Flutter 代码是不切实际的。 其中一些是特定于平台的,例如 Android 和 iOS。
  • 选择足够的 Web 功能子集:随着时间的推移,Web 平台会累积重复的功能。 例如,你可以使用 HTML + CSS,SVG,Canvas 和 WebGL 绘制图形。

从 Dart 诞生之初,它就一直在编译 JavaScript。 现在有许多重要的应用都从 Dart 编译为 JavaScript,并在生产环境中运行。 Flutter 的编译策略依赖于同样的基础设施。

当我们开始探索时,我们面临着 UI 渲染的几种选择。 我们很快意识到,要想支持的特定 Flutter 层,决定了我们将用什么 Web 技术。 我们构建了三个原型:

  • 仅仅是 Widgets(组件):这个原型实现了 Flutter 的 widget 框架,并提供了一组核心布局 widget 作为构建自定义 widget 的基础。 对于布局和定位,它依赖于 Web 的内置功能,例如 flexbox,grid 布局,浏览器滚动(通过 overflow:scroll 实现)等。
  • Widgets + 自定义布局:此原型包括 Flutter 的布局系统(由 RenderObject 提供),但将渲染对象直接映射到 HTML 元素。
  • Flutter Web Engine:这个原型保留了 dart:ui 之上的所有层,并提供了一个在浏览器中运行的 dart:ui 实现。

One of the most valuable features of Flutter is that it is portable across platforms. While you can (and sometimes are encouraged to) write custom platform-specific code, the code that does not need to be different across platforms can be shared. This allows writing applications targeting multiple platforms with a single codebase.

Flutter 最有价值的功能之一是它可以跨平台移植。 你完全可以(有时甚至被鼓励)编写自定义的特定平台代码,代码无需跨平台定制即可共享。 意味着使用单个代码库就可以编写面向多个平台的应用。

在尝试将几个示例应用移植到 Web 之后,我们意识到原型 #1 和 #2 不能提供 Flutter 开发者喜欢的可移植性级别。 因此,我们决定使用 Flutter Web Engine 设计的原型 #3,因为它有着平台之间最高的框架级代码重用:
image.png

Flutter的Web架构 (Hummingbird)

既然我们知道我们想要实现整个 dart:ui API,我们需要选择一组 Web 技术来构建。 Flutter 一次渲染一帧 UI。 在每个帧内,Flutter 会构建 widgets,执行布局,最后在屏幕上绘制它们。

构建 Widgets

Widget 构建机制不依赖于应用运行的环境。该过程只是实例化内存中的对象,跟踪其状态、以及状态变更何时计算系统低级别的最小更新,布局和绘制等。 将此部分移植到 Web 上非常简单。 在 Dart 团队用 dart2js 中实现了 super-mixin 支持之后,编译器将所有 widget 和 widget frame 都编译成了 JavaScript,几乎没有 issue 产生。

布局

布局系统有点棘手。 最大的挑战是文本布局。 除了 Center,Row, Column,Stack,Scrollable,Padding,Wrap 等之外的所有内容都由框架布局,因此无需修改即可编译到 Web。

在 Flutter 中,你可以创建 Paragraph 对象并调用其 layout() 方法来实现文本布局。 不幸的是,Web 缺少直接的文本布局 API。 我们用来测量文本布局属性的技巧是:先让浏览器布局,然后从 DOM 元素中读回相关属性。

布局文本段落时,Flutter 会测量段落的高度,宽度,最大内在宽度,最小内在宽度以及字母和表意基线。 这些属性如下所示。
image.png

Paragraph layout attributes

你可以在 Flutter 的 Paragraph 文档中找到更多详细信息。

要测量这些属性,我们首先在 HTML DOM 元素中放置一个段落,然后读取元素的维度。 这会引起浏览器布局。 例如,要获取元素的宽度和高度,我们调用 offsetWidth 及 offsetHeight。 为了测量基线,我们将段落放置在一个元素中,该元素配置为使用 flex 行进行布局。 在段落旁边,我们放置另一个名为 probe 的元素。 因为 probe 与文本的基线对齐,所以调用 getBoundingClientRect 就可以得到基线。 我们使用类似的技巧来测量最小和最大固有宽度。

  • Painting(绘制) *
    不得不提的是,我们得绘制 widgets。 对这个区域的探索最是麻烦,它仍然是我们的研究中最活跃的领域。 在框架最后,我们所有的 widget 都需要在屏幕上绘制成像素。 在浏览器中,这意味着它们必须归结为 HTML / CSS,Canvas,SVG 和 WebGL 的某种组合。

我们还没有看过 WebGL,主要是因为它级别较低,并且要求我们重新实现浏览器已经可以做的事情,例如文本布局和光栅化 2D 图形。另一个原因是我们还没有弄清楚非 Flutter 组件与 WebGL 如何结合才能实现可访问性,文本选择,和组合。

我们的早期原型为每个 RenderObject 生成了一个 HTML 元素。 结果符合预期,但最后事实却证明 API 的变化太大了。 我们必须用 Flutter 维持一个巨大的代码增量,所以我们搁置了这个想法。

我们目前正在同时探索两种方法:

  • HTML+CSS+Canvas
  • CSS Paint API

HTML+CSS+Canvas

基于这种方法,我们将框架生成的图片分类为使用 HTML + CSS 表达的图片以及使用 Canvas 2D 表达的图片。然后,我们输出结合了 HTML,CSS 和 2D 画布的 HTML DOM。
我们更喜欢 HTML + CSS,因为它受浏览器的显示列表支持。这意味着我们可以把图片的光栅化优化留给浏览器的渲染引擎去做。并且,我们还可以应用任意变换,尤其是旋转和缩放,而不必担心像素化。我们将此画布实现称为 DomCanvas。

如果我们无法使用 HTML + CSS 表达图片,我们会用 Canvas。 Canvas 2D 允许我们绘制几乎所有的 Flutter 绘图命令。如果将 Flutter 的 Canvas 与 Web 的 CanvasRenderingContext2D 进行比较,你会发现许多相似之处。在 Canvas 上绘画很高效,因为它不会创建需要随时间维护的可变树节点,如 HTML DOM 或 SVG。

2D Canvas 的一个挑战是浏览器将其表示为位图,即存储 Width x Height 像素的内存缓冲区。因此,缩放 canvas 会导致像素化。如果缩放导致图片大小变化,我们也需要调整 canvas 大小。我们发现分配 canvas 操作相当昂贵,因此方案改成调整它们的大小。最重要的是,当将多个 canvas 合成到同一页面上时,浏览器必须执行栅格合成,这也需要配置。合成栅格与显示列表的方式不同。你可以将多个显示列表绘制到同一个内存缓冲区中。我们调用 Canvas 2D 支持的 canvas 实现 BitmapCanvas。我们正在发掘使位图 canvas 更高效的方法。

为了表达 Flutter 的不透明度,变换,偏移,剪辑矩形和其他图层,我们使用纯 HTML 元素。例如,不透明度层变为 元素,其上具有 opacity CSS 属性,变换图层变为带有 transform CSS 属性的 元素,剪辑 rect 变为使用 overflow: hidden 的 。

完成所有操作后,框架将作为 HTML 元素树呈现在页面上,其中 DomCanvas 和 BitmapCanvas 作为叶节点。举个例子:
image.png

Sample HTML DOM structure of a frame

Flutter Engine 中的等效 Flutter layer tree(称为 flow layer)如下所示:
image.png

Sample Flutter Engine layer structure

它们的结构非常相似。 最大的区别是,在 Web 上,我们必须根据内容选择不同的图片实现。

HTML + CSS + Canvas 适用于所有现代浏览器。 但是,我们已经在展望未来:

CSS Paint API
CSS Paint 是一个新的 Web API,是 Houdini 的更大组成部分。 Houdini 是多个浏览器厂商合作的项目,旨在向开发者展示 CSS 引擎的某些部分。 特别的是,CSS Paint API 允许开发者在这些元素请求绘制时将自定义图形绘制成 HTML 元素。 例如,你可以将元素背景的绘制分配给自定义 CSS 绘制器。 它与 canvas 非常像,但有以下重要区别:

这个绘画不是由核心 JavaScript 独立完成的,而是由一个叫做 paint worklet 的东西完成的。 它有点像 web worker,因为它有自己的内存空间。 它会在 DOM 更改提交之后,在浏览器的绘制阶段执行绘制工作。

CSS paint 由显示列表支持,而不是位图。 这真是两全其美 - 2D canvas 般的绘画效率和无像素化。

目前 CSS paint 不支持绘制文本。

在撰写本文时,Chrome 和 Opera 是唯一在正式版本中支持 CSS Paint 的浏览器。 而其他浏览器正处于发布各自实现的不同阶段。

我们在 Flutter for Web 中对 CSS Paint API 进行了实验性支持,它已经展现出良好的结果,特别是在性能方面。 我们的实现只是将 paint 命令序列化为自定义 CSS 属性。 paint worklet 读取这些命令并执行它们。 我们使用像

这样普通的 HTML 元素来渲染文本。

我们当前的序列化机制不是特别有效 - 它是一个嵌套列表转换成的 JSON 树 - 但 Houdini 项目的一部分是添加对类型化数组的支持。 当它可用时,我们会将绘制命令编码为类型化数组而不是 JSON 字符串。 类型化数组是可转移的,这意味着它们可以通过引用从主 JavaScript 传递到 paint worklet,而不复制内存。

协同和嵌入

从 Flutter 调用 Dart 库

Flutter Web 应用可以访问当前在 Web 上运行的所有 Dart 库。

从 Flutter 调用 JavaScript 库

Flutter Web 应用完全支持 Dart 的 JS-interop 软件包:package:js和 dart:js。

在 Flutter Web 应用中使用 CSS

目前,Flutter 假定完全控制网页的正确性和性能。 例如,我们只使用遵循某些性能指南的一小部分 CSS,例如 https://csstriggers.com/。 在页面上随意使用 CSS 可能会导致 Flutter 出现不可预期的后果。

在 Flutter for Web 应用中避免使用 CSS 的另一个原因是,在设计时,Flutter 需要在渲染框架时知道所有布局属性。 CSS 充当黑盒。 例如,如果要显示可滚动的窗口 widget 列表,则必须实例化并为所有 widgets 生成 HTML 并应用必要的 CSS 属性(例如,flex-direction row 和 overflow:scroll)。 然后浏览器将所有内容都布局并将其渲染到屏幕上。 应用代码不参与布局过程。

最后,本着保持 Flutter 代码可跨平台移植的精神,我们尽量避免使用 CSS,因此我们可以在 Android 和 iOS 上本机运行相同的代码。

将 Flutter 嵌入现有的 Web 应用中

我们还没有为此添加适当的支持,但我们打算在将来探索。 我们正在考虑的方法是 和 shadow DOM。

在 Flutter 中嵌入非 Flutter 组件

我们还未支持在 Flutter Web 应用中嵌入非 Flutter 组件 - 自定义元素、React 组件、Angular 组件,但我们打算在将来探索。 有可能是使用平台视图将外部内容放入 Flutter Web 应用中。需要考虑的是外部内容可能对应用的性能和正确性产生影响。 因为非 Flutter 组件可能包含任意 CSS,如上所述,它可能会有问题。 需要更多的研究。

可移植性

我们的目标是尽可能多地将框架移植到 Web 上。 但是,这并不意味着任何 Flutter 应用将在 Web 上运行而不更改代码。 Flutter Web 应用仍然是一个 Web 应用; 它在浏览器中被沙箱化,只能执行 Web 浏览器允许的操作。 例如,如果你的 Flutter 应用使用 Web 未实现的本机插件(例如 ARCore),你将无法在 Web 上运行该应用。 同样,也无权限直接访问文件系统或低级网络。

当前状态

我们构建了足够的 Web 引擎来渲染大部分 Flutter Gallery。 我们还未移植 Cupertino widgets,但所有 Material widgets,Material Theming,以及 Shrine 和 Contact Profile 演示应用均已运行在 Web 上。

Flutter running in desktop Chrome 演讲视频
https://www.youtube.com/watch?v=5IrPi2Eo-xM

源代码在哪里?
我们计划很快开源这个项目,并很高兴与开源社区分享。 该项目最初是作为 Google 内部源代码树的一项探索而开始的。 待代码稳定后,我们打算将开发转移到 GitHub,我们有机会将其从内部基础架构中剥离出来。 与此同时,如果您在 github.com/flutter 组织下看到与 Web 相关的 pull 请求,请不要感到惊讶!

结论

希望这篇文章能让你了解我们正在解决的问题,以帮助 Flutter 在 Web 上更好运行。 欢迎表达您的观点和意见。

请继续关注 Google I/O 2019!

英文原文:

https://medium.com/flutter-io/hummingbird-building-flutter-for-the-web-e687c2a023a8

目录
相关文章
|
20天前
|
前端开发 JavaScript 安全
前端性能调优:HTTP/2与HTTPS在Web加速中的应用
【10月更文挑战第27天】本文介绍了HTTP/2和HTTPS在前端性能调优中的应用。通过多路复用、服务器推送和头部压缩等特性,HTTP/2显著提升了Web性能。同时,HTTPS确保了数据传输的安全性。文章提供了示例代码,展示了如何使用Node.js创建一个HTTP/2服务器。
36 3
|
26天前
|
移动开发 开发者 HTML5
构建响应式Web界面:Flexbox与Grid的实战应用
【10月更文挑战第22天】随着互联网的普及,用户对Web界面的要求越来越高,不仅需要美观,还要具备良好的响应性和兼容性。为了满足这些需求,Web开发者需要掌握一些高级的布局技术。Flexbox和Grid是现代Web布局的两大法宝,它们分别由CSS3和HTML5引入,能够帮助开发者构建出更加灵活和易于维护的响应式Web界面。本文将深入探讨Flexbox和Grid的实战应用,并通过具体实例来展示它们在构建响应式Web界面中的强大能力。
34 3
|
16天前
|
存储 调度 数据安全/隐私保护
鸿蒙Flutter实战:13-鸿蒙应用打包上架流程
鸿蒙应用打包上架流程包括创建应用、打包签名和上传应用。首先,在AppGallery Connect中创建项目、APP ID和元服务。接着,使用Deveco进行手动签名,生成.p12和.csr文件,并在AppGallery Connect中上传CSR文件获取证书。最后,配置签名并打包生成.app文件,上传至应用市场。常见问题包括检查签名配置文件是否正确。参考资料:[应用/服务签名](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-signing-V5)。
48 3
鸿蒙Flutter实战:13-鸿蒙应用打包上架流程
|
8天前
|
缓存 安全 网络安全
HTTP/2与HTTPS在Web加速中的应用
HTTP/2与HTTPS在Web加速中的应用
|
11天前
|
SQL 安全 前端开发
PHP与现代Web开发:构建高效的网络应用
【10月更文挑战第37天】在数字化时代,PHP作为一门强大的服务器端脚本语言,持续影响着Web开发的面貌。本文将深入探讨PHP在现代Web开发中的角色,包括其核心优势、面临的挑战以及如何利用PHP构建高效、安全的网络应用。通过具体代码示例和最佳实践的分享,旨在为开发者提供实用指南,帮助他们在不断变化的技术环境中保持竞争力。
|
21天前
|
前端开发 安全 应用服务中间件
前端性能调优:HTTP/2与HTTPS在Web加速中的应用
【10月更文挑战第26天】随着互联网的快速发展,前端性能调优成为开发者的重要任务。本文探讨了HTTP/2与HTTPS在前端性能优化中的应用,介绍了二进制分帧、多路复用和服务器推送等特性,并通过Nginx配置示例展示了如何启用HTTP/2和HTTPS,以提升Web应用的性能和安全性。
23 3
|
21天前
|
前端开发 JavaScript API
前端框架新探索:Svelte在构建高性能Web应用中的优势
【10月更文挑战第26天】近年来,前端技术飞速发展,Svelte凭借独特的编译时优化和简洁的API设计,成为构建高性能Web应用的优选。本文介绍Svelte的特点和优势,包括编译而非虚拟DOM、组件化开发、状态管理及响应式更新机制,并通过示例代码展示其使用方法。
36 2
|
22天前
|
测试技术 持续交付 PHP
PHP在Web开发中的应用与最佳实践###
【10月更文挑战第25天】 本文将深入探讨PHP在现代Web开发中的应用及其优势,并分享一些最佳实践来帮助开发者更有效地使用PHP。无论是初学者还是有经验的开发者,都能从中受益。 ###
44 1
|
22天前
|
负载均衡 监控 算法
论负载均衡技术在Web系统中的应用
【11月更文挑战第4天】在当今高并发的互联网环境中,负载均衡技术已经成为提升Web系统性能不可或缺的一环。通过有效地将请求分发到多个服务器上,负载均衡不仅能够提高系统的响应速度和处理能力,还能增强系统的可扩展性和稳定性。本文将结合我参与的一个实际软件项目,从项目概述、负载均衡算法原理以及实际应用三个方面,深入探讨负载均衡技术在Web系统中的应用。
48 2
|
20天前
|
存储 Dart
Flutter&鸿蒙next 实现一个计算器应用
本文介绍了如何使用 Flutter 创建一个简单的计算器应用,包括基本的加减乘除运算。文章详细讲解了界面布局、计算逻辑和状态管理的实现步骤,通过具体的代码示例展示了如何构建计算器界面、处理用户输入和显示计算结果。
68 0