查看原文
其他

Flutter 最新进展与未来展望

Flutter 谷歌开发者 2019-11-01

演讲者 / 董韬,Google Flutter 团队,高级研究员 (开发者体验)。2009 到 2014 年在密歇根大学从事人机交互方向的博士研究工作,多次担任 ACM 会议组委会成员和论文审稿人。2016 年下半年加入 Flutter 团队,目前负责 Flutter 的 API 可用性,开发工具设计,以及文档可用性的研究工作。



1. Flutter 及其在中国的发展状况


相信关注我们的开发者朋友已经很熟悉 Flutter 了: 它是 Google 主导研发的一个多平台 UI 工具包,在不久前的 I/O 大会上已经进入 1.5 版本。其优点可以概括为四个方面:


美观

Flutter 构建应用时,可以让开发者对 UI 实现像素级的控制。这也意味着原本的设计意图可以被完美地执行出来,从而将品牌个性忠实地传达给用户。


快速

Flutter 的 UI 渲染性能很好。在生产环境下,Flutter 将代码编译成机器码执行,并充分利用 GPU 的图形加速能力,因此使用 Flutter 开发的移动应用即使在低配手机上也能实现每秒 60 帧的 UI 渲染速度。

Flutter 同时做到 “美观” 和 “快速” 的原因,在于其架构本身。Flutter 引擎使用 C++ 编写,包括高效的 Skia 2D 渲染引擎,Dart 运行时和文本渲染库。这个引擎使得 Flutter 框架可以自由、灵活、高效地绘制 UI 组件。而应用开发者则可以用 Flutter 框架来轻松实现各种设计语言和动画效果。


高效

△ 有状态热重载使得开发者在修改界面后能立即看到相应的变化

对开发者来说,使用 Flutter 开发应用十分高效。Flutter 广受好评的 Hot Reload (热重载) 功能可以在 1 秒内实现代码到 UI 的更新,使得开发操作周期被大幅缩短。另外,热重载能够在执行的时候保留应用的当前状态 (即 Stateful),比如您可能在修改一个导航结构里的子页面,保留状态的热重载可以让您不需要重新从起始页一路点击回到这个子页面,而是在代码修改完成后即刻看到结果。 


开放

△ Flutter 的 GitHub repo

Flutter 是开放的,它是一个完全开源的项目。全球的开发者都可以免费使用和拓展 Flutter 的源代码,并为 Flutter 的生态和文档作贡献。 我们已经看到许多中国开发者活跃在社区中,并为 Flutter 做出了坚实的贡献。


Flutter 发展状况

在 StackOverflow 2019 年的全球开发者问卷调查中,Flutter 被选为最受开发者欢迎的框架之一,超过了 TensorFlow 和 Node.js。

全球已经有很多大家熟悉的品牌采用了 Flutter,包括很多国内的知名公司。比如阿里巴巴有多款移动应用已经上线 Flutter 版本

在中国,Flutter 的开发者社区非常活跃。社区贡献了大量高质量的技术文章,Flutter 官方文档的翻译,还组织了许多线上线下的活动。在今年 I/O 前举办的全球 Flutter Create 大赛中,来自中国广东的胡泽标凭借一个特别精致的罗盘应用摘得了全球大奖

△ Flutter 中文社区 (flutter.cn)

△ Flutter Create 全球大奖作品 (flutter.dev/create)

来自多个公司的中国开发者给 Flutter 代码库提交了 Pull Request。特别是阿里的一位开发者,在今年二月的某一个礼拜,他合并 PR 的数量超过了任何一位 Flutter 团队的成员。我们欢迎每一位开发者通过多种形式参与到 Flutter 的开发过程中,包括在 GitHub 上提交 issue 和 PR。

Flutter 在移动端已经证明了其技术和开发体验上的优势。我们希望开发者能够在更多的计算平台上体验到这些优秀的特性。所以我们在上个月的 I/O 大会上开放了 Flutter for Web 的技术预览。在桌面端和嵌入式系统上,我们也已经开始了实验性探索。我们的长期目标是让 Flutter 在所有带屏幕的设备上都可以实现优秀的用户体验——甚至包括充满创意的大型互动装置:

△ 我们纽约团队的同事用 Flutter 制作的大型艺术装置 



2. 多平台愿景: Flutter for Web


△ 来自丹麦的 Reflectly,使用 Flutter 开发的移动和 Web 应用体验几乎完全一致

Flutter for Web 的其中一个目标,就是让开发者可以很容易地复用移动端的代码。关于 Flutter for Web 的很多技术细节,如架构设计以及渲染机制的选择等,我们在去年的《Hummingbird: Web 里的 Flutter》一文里有过详细的介绍,有兴趣的朋友可以深入阅读,下面简单介绍一些 Flutter 在移动端和 Web 端里的异同:


Flutter 在 Web 上的技术实现

如上图所示,在移动端中,C++ 引擎在 Flutter 里面通过 dart:ui 这个库被上层的框架调用。Dart:ui 这个库非常底层,它没有 UI 组件的概念,不知道 UI 布局,也不知道动画。它只做一件事情, 就是把上层框架产生的图片对象画到屏幕上。

Flutter for Web 保留了 Flutter 框架的代码来最大化复用移动端和 Web 端的代码。但是为了能在浏览器里面运行 Flutter 程序,Web 端的 Flutter SDK 用 Flutter Web engine 代替了移动端的 C++ Flutter engine。这个 Flutter Web engine 提供了一个 dart:ui 库在 Web 平台上的实现,它做的事和移动端 dart:ui 很相似——把 Flutter 上层框架产生的图片对象 “画” 到网页上。

具体来说,Flutter Web engine 会优先使用 HTML 和 CSS 来绘制图像。这样做可以利用到浏览器自身的性能优化机制,也不用担心缩放页面的时候出现像素化的问题。只有在少数情况下,Flutter Web engine 会用 Canvas 来实现绘图。我们目前也在尝试使用 CSS Paint API 来代替 Canvas 绘图以提高性能。但是这个 API 还比较新,有待各大浏览器提供支持。


但回到上层的逻辑时,代码复用的优势就开始显现了—— Flutter Web engine 使用和移动端一致的 Flutter 框架来构建 widgets 以及实现 UI 布局。另外,我们使用了 Dart2js 这个编译器来将 Dart 语言转换为 JavaScript。


Flutter for Web 的定位和使用场景

第一个使用场景是用 Flutter for Web 开发现有移动应用的 Web 端配套应用。现在有很多产品都是移动优先,但是在某些场景下,移动端多少有一些局限,比如屏幕太小、文本输入麻烦。这个时候很多厂商会开发一个 Web 端配套应用来利用桌面电脑的输入输出设备提高交互效率 (这样的例子很多,比如微信就有 Web 端的配套应用)。

第二个场景是用 Flutter for Web 来利用现有 Flutter 移动项目中的交互元素。上图显示的这个 Flutter 移动应用是 Google Ads。它有很多动态的可交互的图表,实现起来比较复杂。而通过 Flutter for Web 就可以在 Web 端的应用中重用这些代码。类似的交互元素还包括一些小游戏和工具类的应用,比如日历、汽车配置向导和投资组合分析工具等。


性能、开发体验和局限

目前,Flutter for Web 的示例应用在桌面浏览器基本能达到每秒 60 帧的渲染速度。但是在移动浏览器,特别是在低端机型上还有很大的优化空间。


  • Flutter for Web 示例应用

    flutter.github.io/samples


Flutter for Web 在 Widget 这一层和移动端保持高度一致,所以代码的复用能力很好。图像渲染的结果也达到了高度一致。在开发调试的时候可以用 Dart DevTools 并且支持 Hot Reload。虽然在开发体验上还有很多的工作要完成,但目前已经处在一个基本可用的状态。

△ Flutter for Web 的开发体验已经处在基本可用的状态

总的来说,Flutter for Web 目前处在技术预览阶段,不建议在生产环境中使用。我们当前在 Web 端主要的研发工作如下: 


  • 合并代码到 Flutter SDK

  • 保证所有 widgets 都能正确渲染

  • 优化性能

  • 完善对浏览器无障碍功能的支持

  • 添加插件系统来使用现有的 JS 库

  • 加强开发调试工具的可用性


欢迎大家积极试用 Flutter for Web 并提交反馈意见,也请大家耐心地等待我们推出正式版本。



3. 生态建设: 与社区共建的状态管理解决方案


Flutter 采用的是类似 React 的响应式编程模型。UI 在运行时视觉上的变化是由应用的状态来驱动的:

这个状态要放在应用程序的哪个地方,怎么去更新和读取,这就是状态管理问题。我们在官网上把比较主流的状态管理方法和代码包做了一个汇总。开发者可以根据不同应用的具体需求来选择使用: 

  • Flutter 状态管理解决方案清单

    https://flutter.dev/docs/development/data-and-backend/state-mgmt/options


但是,对于没有复杂需求的开发者,或者初学者,我们也着重推荐其中一个状态管理包: provider。这个状态管理方案是由一位 Flutter 社区成员贡献的。


在 provider 发布之前,Google 团队其实做了一个思路上很相似的状态管理包,叫 provide。但是在比较之后,发现这个社区贡献的包可以解决同样的问题而且更加容易上手。后来,我们就决定和这位社区成员合作共同推广和进一步完善 provider。

△ 随着越来越多社区开发者提供的解决方案被采纳和推广,Flutter 的社区也会逐步发展壮大,并走向成熟


package:provider 的工作机制

下面这个示例程序包含三个不同的页面: 登陆、商品列表和购物车。用户在商品列表添加了一些物品之后,购物车里的金额需要自动更新。但是在响应式编程模型下,商品列表页里的代码不能去直接修改购物车里的数据。

  

解决这个问题的一个基本的思路是把两个页面共享的数据放在这个 UI 组件树的上端。Provider 提供了一套机制来管理多个节点共享的应用状态。这个机制由三个基本步骤组成:

首先,我们定义一个继承自 ChangeNotifier 的 CartModel 来负责购物车的数据读写。ChangeNotifier 是 Flutter SDK 中的一个简单的类,它可以用 notifyListeners 这个方法向监听器发送通知。在这个例子里面,每当有一件物品被放入购物车,CartModel 就会通知它的监听器。

然后我们要用到 ChangeNotifierProvider 这个 widget。它可以返回一个特定类型的 ChangeNotifier 并把它提供给下属的 widgets。在这个例子里,我们把 ChangeNotifierProvider 放在 MyApp 上面的节点,这样可以让列表和购物车这两个页面都读写 CartModel 里的数据。我们可以用 Consumer 这个类在需要它的状态的时来获取它的一个实例。

这里我们需要指定要访问的数据类型。在这个示例中,我们使用 Consumer<CartModel> 来指定访问 CartModel 类型。在 Consumer 的 builder 函数里,我们可以拿到一个 CartModel 的实例,并通过它来更新购物车里需要显示的总价格。

△ 在 UI 树的上端用 ChangeNotifierProvider 让 CartModel 这个类能够在商品列表和购物车这两个页面都能够被使用。然后在需要用到 CartModel 的子节点实例化这个类来对其中的状态数据进行操作,从而更新 UI。


  • 进一步了解上述状态管理的实现细节

    https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple



4. 开发体验: "UI as Code"


我们一直很重视 Flutter 的开发体验,所以对提高 Flutter UI 代码的可读性做了一系列的尝试,包括 Dart 语法的改进和对 IDE UI 的改进,这些统称为 UI as Code。


Flutter 的 UI 代码使用 Dart 语言,其优点如下:


  • Flutter 的声明式 UI 编写方法能够直观地描述 UI 结构

  • 不需要为 UI 布局学习额外的语法

  • 不需要维护代码之外的 UI 定义文件


但我们之前的实现方法也有几个值得改进的地方:


  • 复杂 UI 逻辑会使用命令式语法,打破代码结构和 UI 视觉结构的一致性

  • 多层嵌套之后不容易对 UI 的构成做到一目了然


近期我们做出了一些比较成功的改进,包括 Dart 2.3 中加入了针对 UI 编程优化的新语法元素,以及在 IDE 中加入了 Editor UI Guides。


Dart 2.3 中的改进

在 Dart 2.3,我们增加了 Control Flow Elements in Collections,可以在集合数据类型的定义中使用 if 和 for 这样的流程控制元素。另外还增加了对 spread operator 的支持。


我们以一段 Flutter 代码为例,来讲解一下新的语法特性带来的变化:

上面这段代码用来显示一本电子书的目录。这个 UI 采用 Column 这个 widget 实现纵向布局。但是它有一些逻辑,需要用到循环和判断语句。这就导致了它需要把 Column 里面的内容先拼装到 items 这个变量里,然后再放到 Column 的 children 属性上。这里的问题是,最后的这个 Column 在概念上和视觉上都应该是先于它里面的内容出现的。但是由于语法的局限,这个空间关系被反了过来。代码看起来更像是命令式的而不是声明式的。


现在看看使用 Dart 2.3 新语法之后这段代码的写法:

首先,Column 可以按照我们直观的想法放在结构的最上层,然后 “下一页” 的逻辑也可以直接写到 List 的定义里面。每一个章节的名字可以用 for 元素和 spread operator 直接在 List 的定义里获得。改写之后的代码更精简,且更接近这个 UI 的直观描述。


IDE 中的改进

针对开发者日常使用的 IDE,我们希望能在不改变代码的前提下,让开发者对 UI 的结构一目了然。这方面我们做出的改进就是 Editor UI Guides:

这个功能现在在 IntelliJ 和 Android Studio 最新版的插件里已经默认打开了,在 VS Code 里需要在设置中打开。在发布之后,我们得到了非常积极正面的用户反馈。

△ 我们还在测试更多实验性的功能,来不断改进 Flutter 开发者读写代码的效率。

△ Dart 2.0 时允许省略 new 关键字,使得可读性得到了很大改善;2.3 新加入的 UI as Code 则从代码和 IDE 的层面进一步提升了开发者的效率。这样的优化还会继续。 



5. Flutter/Dart 近期展望


开发者们对 Flutter 的呼声很高,我们也一直在加大投入。今年我们针对 Flutter 的主要工作内容已经在 Github wiki 上公开。大家可以前往查看详情。

  • Dart GitHub repo

    https://github.com/flutter/flutter/wiki/Roadmap


Dart 语言的 GitHub repo 上也有新特性的完整文档。"accepted" 目录中的为工程实施阶段,"working" 目录中的为设计阶段,欢迎大家保持关注。

  • Dart GitHub repo

    https://github.com/dart-lang/language


查看详细内容 Dart 语言也在积极地开发对 non-nullable types 的支持。Extension methods 也在 Dart 语言的项目日程上。Dart runtime 会增加对直接调用 C/C++ 代码的支持 (目前已经有一个技术预览)。


Flutter 的报错信息会更容易理解。我们最近对 Flutter 的报错 API 做了很大的改进,使得 Flutter 能够向 IDE 输出带有结构信息的错误报告,IDE 可以有选择性地突出最重要的信息并缩略不常用的调试数据来避免信息过载。


其他的投入还包括持续优化 Flutter for Web,以及完善桌面 UI 的基本交互方式,比如对剪贴板、鼠标右键操作的支持。


最后,欢迎大家积极尝试 Flutter,并在社区中发出声音,提出问题和改进建议,以及贡献代码。我们相信随着越来越多的开发者在社区中活跃,Flutter 会将美观、高效的用户体验,以及高效、开放的开发者体验带到更多的屏幕上,让更多人感受到个性十足且功能强大的产品。



 点击屏末 | | 了解 Flutter 更多详细信息



推荐阅读




    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存