众所周知,在开发 Flutter 时,我们希望通过单一代码库支持 Flutter 在各类设备 (包括 iOS、Android、Windows、Linux、macOS 和 Web) 上的应用,并实现原生编译和游戏级别的视觉效果。Flutter 也已经在 Google 内部广泛用于 Assistant、Stadia、Cloud Search 以及 Blogger 等项目。在 Google 之外,字节跳动、Grab、Nubank 以及 MGM Resorts 等公司也都已采用 Flutter,并通过 Flutter 提升了生产力和灵活性。
很多人都想了解 Flutter 在桌面操作系统 (包括 Windows、macOS 和 Linux) 上的进展: 据调查和 GitHub 数据显示,Flutter 的桌面支持一直是最受期待的新功能之一。未来数周内,我们将为大家带来更多这方面的进展;同时,我们认为有必要先展示来自各个产品功能团队的成果 (这些成果最终会整合进完整的项目中),并搜集大家的反馈。虽然桌面支持目前已推出技术预览版本,但我们仍然有大量工作需要完成。
发布模式
继 macOS 之后,我们还为 Flutter 增加了 Windows 和 Linux 系统的 Profile Mode (性能模式) 和 Release Mode (发布模式) 。例如,如果您运行的是最新版 Flutter,现在可以使用 flutter build windows 命令,将 Flutter 应用编译为 Windows 可执行文件。此操作使用我们在生产环境中的 AOT 编译器来创建原生 x64 机器代码,而这些代码可分发至那些未安装 Flutter 的设备上。
- Windows 发布模式
https://github.com/flutter/flutter/issues/38477
- Linux 发布模式
https://github.com/flutter/flutter/issues/38478
- AOT 编辑器
https://en.wikipedia.org/wiki/Ahead-of-time_compilation
桌面级体验
无论您要构建的是独立可执行文件还是 Web 应用,桌面级体验都有其独特的属性: 桌面窗口通常采用横屏模式并可以调整大小、输入通常由物理键盘和鼠标完成,而不是通过屏幕键盘和触摸操作,同时,控件也是基于不同的屏幕内容密度进行的优化。
在框架层面,我们对 Flutter 进行了多项变更以支持桌面级体验。
- 在最新版本 Flutter 中创建新项目时,您会看到默认模板包含了对 visualDensity 属性的引用,该属性允许控件根据目标平台调整其密度,从而在桌面平台上实现更紧凑的间距。其中一个例子就是 TextField: 它可根据指定的密度提供紧凑 (compact)、舒适 (compact)、标准 (standard) 三种间距。
- 我们优化了对鼠标和键盘输入的支持,其中包括 Windows 上的原始键代码、鼠标右键操作、光标更改和滚轮支持。
- 您现在可以 (通过 Platform 类) 查询特定平台,Windows、macOS 和 Linux 平台均可提供恰当的查询结果。
- 我们在最新版本中添加了一个 NavigationRail widget,该 widget 专为在桌面设备和平板电脑上打造桌面级体验而设计。
- visualDensity
https://api.flutter-io.cn/flutter/material/ThemeData/visualDensity.html
- 桌面端需要更高的屏幕内容密度
https://github.com/flutter/flutter/issues/43350
- TextField
https://api.flutter-io.cn/flutter/material/TextField-class.html
- 为 TextField 实现 visualDensity
https://github.com/flutter/flutter/pull/51438
- 在 Windows 上实现键盘映射
https://github.com/flutter/flutter/issues/52264
支持光标更改
https://github.com/google/flutter-desktop-embedding/issues/376
- NavigationRail widget
https://api.flutter-io.cn/flutter/material/NavigationRail-class.html
外部函数接口
Dart 团队一直在努力完善外部函数接口 (Foreign Function Interface, FFI) ,以加速平台集成的进程。对于基于 C 语言的 API,dart:ffi 库提供了一种直接绑定到原生代码的机制;Dart 运行时则提供了在 Dart 对象支持的堆上分配内存并调用动态链接库的功能。
- 通过 dart:ffi 实现 C 语言互操作
https://dart.cn/guides/libraries/c-interop
以下代码片段为 Windows 系统环境中的一个简单示例,该示例完全使用 Dart 代码来调用传统的 Win32 MessageBox() API:
- typedef MessageBoxNative = Int32 Function(
- IntPtr hWnd, Pointer<Utf16> lpText, Pointer<Utf16> lpCaption, Int32 uType);
- typedef MessageBoxDart = int Function(
- int hWnd, Pointer<Utf16> lpText, Pointer<Utf16> lpCaption, int uType);
- final user32 = DynamicLibrary.open('user32.dll');
- final win32MessageBox =
- user32.lookupFunction<MessageBoxNative, MessageBoxDart>('MessageBoxW');
- void showMessageBox(String message, String caption) => win32MessageBox(
- 0, // No owner window
- Utf16.toUtf16(message), // Message
- Utf16.toUtf16(caption), // Window title
- 0 // OK button only
- );
- …
- showMessageBox('Test Message', 'Window Caption'); // call just like any other Dart function
- 完整示例
https://gist.github.com/timsneath/181092c75864001ca37b1b1495b9b396
在上例中,我们通过两个 typedef 分别体现了方法的原生和 Dart 版本形式。完成这些定义后,我们便可通过 lookupFunction() 方法加载 Windows 动态链接库 (DLL),lookupFunction() 负责将方法签名映射到底层的原生入口,DLL 则负责提供方法的实现。最后,我们可以选择添加一个简单的惯用包装器,使其便于从其他 Dart 代码进行访问,运行结果如下:
△ 使用 Win32 MessageBox API 的 Windows 简单示例应用
当然,您不必亲自完成这项工作: 有人很可能已经处理好了您要使用的 API。您可以阅读我们的官方文档,详细了解如何在 Flutter 中使用 FFI。
- 访问常用 Win32 API 的 Dart 库
https://pub.flutter-io.cn/packages/win32
- 官方文档: 使用 dart:ffi 调用原生代码
https://flutter.cn/docs/development/platform-integration/c-interop
更新插件模型
从设计上说,Flutter 本身的核心很小。Flutter 并没有在框架中提供海量内容,而是通过插件和 package (无论是直接来自 Flutter 团队还是来自更广泛的生态系统中的贡献者) 来实现与底层操作系统的集成。
但是,随着 Flutter 对移动设备、Web 和桌面端的支持日益增多,为每个目标平台开发插件也变得越来越具有挑战性。更可能出现的情况是: 一个插件需要多个掌握各自平台专业知识的作者通力合作,才能完成开发。
一个可行的做法是,在核心插件中定义出通用接口,并在各个平台上独立完成具体的实现。因此,正如近期一篇关于现代插件开发的博文所述,我们最近对插件的架构进行了调整,使多个作者能够更加轻松地合作完成各个平台的开发。基于目前的进展,插件现在可以明确地声明其所支持的具体平台。
- 博文: 现代 Flutter 插件开发
https://medium.com/flutter/modern-flutter-plugin-development-4c3ee015cf5a
- 指定插件所支持的平台
https://flutter.cn/docs/development/packages-and-plugins/developing-packages#plugin-platforms
我们已经开始使用此模型来构建一些核心插件,您也可以在 flutter/plugins repo 中找到一些基于这个联合开发模型的早期示例。
- flutter/plugins repo
https://github.com/flutter/plugins/tree/master/packages/path_provider
请注意,Windows 和 Linux 插件 API 仍在开发之中,因此,虽然我们鼓励大家多多探索,但目前尚未准备好用于通用生产环境。我们也致力于在 pub.dev 上添加桌面平台标签。
在 Windows 上运行: Win32 和 UWP
在 Windows 上进行开发有一个很有趣的点在于,我们会试验各种架构方法。在任何平台上,Flutter 应用其实都是内嵌于一个很小的容器应用 (即 "embedder") 中的,这种做法类似 Unity 等游戏引擎。这种特定于平台的容器应用负责提供入口,与底层操作系统进行协调以使用表面渲染、无障碍功能和输入等服务,并管理消息事件循环。
Windows 提供了两种不同的方法来创建这个容器应用。第一个是使用成熟的 Win32 编程模型来创建 Flutter 内容的入口;这为 Windows 7 等平台提供了最大的向后兼容性,同时能构建出符合诸多开发者预期的标准 EXE 文件。第二个则是现代 UWP 应用模型,推荐在 Windows 10 上使用。这种方法拥有更广阔的想象空间,比如能够帮助开发者将 Flutter 支持扩展至 Xbox 等设备或即将推出的 Windows 10X 操作系统。
- Windows 10X
https://docs.microsoft.com/en-us/dual-screen/windows/get-dev-tools
我们一直与多位社区贡献者一起探讨本文所述的不同解决方案。同时我们很期待与微软紧密合作,共同完成高质量的解决方案。Surface 系列设备正不断扩展,现在其产品已涵盖 Android 和 Windows。我们认为 Flutter 可为微软提供非常有吸引力的平台,横跨其全部产品阵容并提供卓越的原生体验。
尽情探索桌面体验
桌面体验目前仍处于技术预览版状态,而 API 和工具尚不稳定。在将桌面支持升级到稳定版本之前,我们仍将持续跟进大量需要完成的必要工作,包括改进无障碍功能和本地化支持。
如果您想要试用,则需要使用开发版本。Windows 和 Linux 支持目前仅在 master 渠道上可用,我们也在这个渠道中积极进行着 Flutter 的开发。macOS 在 dev 渠道上可用,虽然稳定性较好,但不建议用于生产环境。您可以在 Flutter master 渠道或 dev 渠道之间进行切换,然后使用以下任一命令来为您使用的平台提供支持:
- C:\flutter> flutter config --enable-windows-desktop
- $ flutter config --enable-macos-desktop
- $ flutter config --enable-linux-desktop
一些积极探索的开发者已经 "尝鲜",在桌面环境中利用 Flutter 创建应用。我们见证的首批 Flutter macOS 桌面应用便包括 Sharezone,这是一款针对德国教育市场的学生管理应用,最初创建的是移动应用版本,但最近增加了网页和桌面版本。
△ Sharezone Schulplan: 一款面向学生、教育工作者和家长的应用,用于跟踪家庭作业、学习小组和课程表等情况
- Sharezone
在接下来的数周里,我们将分享更多关于桌面支持的信息;同时,我们也期待着听到您的反馈。如果您是插件作者,我们建议您开始评估将自己的插件适配至即将到来的这些桌面平台所需要的开发工作;如果您已发布应用,不妨尝试将应用作为桌面应用运行,并告诉我们您的运行情况。