Flutter 2 有什么新功能?
已发表: 2022-03-10去年,我在 Smashing Magazine 上写了两篇关于在 Web 和桌面平台上使用 Flutter 的文章。 第一篇文章是对 Web 和桌面开发的一般介绍,重点是构建响应式 UI; 第二篇文章是关于您在尝试开发可在多个平台上运行的 Flutter 应用程序时可能面临的挑战。
那时,Flutter 团队认为 Flutter 对非移动平台的支持并不稳定且可用于生产,但现在情况发生了变化。
Flutter 2 来了
3 月 3 日,Google 举办了 Flutter Engage 活动,Flutter 2.0 发布。 这个版本确实是一个真正的 2.0 版本,许多变化有望使 Flutter真正为超越移动应用程序开发做好准备。
理解 Flutter 2.0 为何重要的核心变化是, Web 开发现在正式成为稳定通道的一部分,桌面支持也将很快出现在稳定通道上。 事实上,它目前以类似发布候选的形式启用,作为稳定通道中的早期发布 beta 快照。
在公告中,谷歌不仅暗示了 Flutter 的未来会是什么样子。 还有一些实际例子表明,大公司已经在开发 Flutter 应用程序,用性能更好的应用程序替换他们现有的应用程序,让开发人员更有效率。 例如,世界上最大的汽车制造商丰田现在将使用 Flutter 在他们的汽车上构建信息娱乐系统。
另一个有趣的公告——展示了 Flutter 作为跨平台 SDK 的改进速度有多快——是 Canonical 的公告,除了使用 Flutter 开发新的 Ubuntu 安装程序外,他们还将使用Flutter 作为构建桌面应用程序的默认选项。
他们还发布了 Ubuntu 的 Yaru 主题的 Flutter 版本,我们将在本文后面使用它来构建一个 Flutter 桌面应用程序,该应用程序在 Ubuntu 桌面上看起来很完美,同时还使用了更多新的 Flutter 功能。 您可以查看 Google 的 Flutter 2 公告以获得更完整的图片。
让我们看一下 Flutter 2.0 版进入稳定通道的一些技术更改,并使用 Flutter构建一个非常简单的示例桌面应用程序,然后我们就可以和不能使用 Flutter 的特定项目类型得出一些结论现在。
更大设备的一般可用性更改
根据公告,Flutter 已经进行了许多更改,以便为非移动设备提供更好的支持。
例如,Web 和桌面应用程序所需的一个明显的例子是滚动条,直到现在必须使用第三方包或通过自己实现它来完成。
现在有一个内置的Scrollbar
可以直接适合您的应用程序,准确查看滚动条在特定平台中的外观:有或没有轨道,可以通过单击轨道进行滚动,例如,这是如果您希望您的用户在使用 Flutter 应用程序时从一开始就感到宾至如归,那么这将是巨大的。 您还可以对其进行主题化和自定义。
当应用程序的内容可滚动时,看起来 Flutter 在某些时候会自动显示合适的滚动条。
同时,您可以使用您选择的滚动条小部件包装任何可滚动视图,并创建一个ScrollController
以添加为滚动条和可滚动小部件的controller
(如果您从未使用过ScrollController
,您可以像TextEditingController
一样使用对于TextField
)。 您可以在本文后面的桌面应用程序示例中看到使用常规 Material 滚动条的示例。
Flutter Web 更改
网页版 Flutter 已经是一种非常可用的形式,但存在性能和可用性问题,这意味着它从来没有像移动端 Flutter 那样完美。 随着 Flutter 2.0 的发布,它有了很多改进,尤其是在性能方面。
编译目标以前非常实验性且难以用于渲染您的应用程序(使用 WebAssembly 和 Skia),现在称为CanvasKit 。 从在移动设备上本地运行 Flutter 应用程序到在浏览器中运行它,它已经过改进以提供一致且高性能的体验。
现在,默认情况下,您的应用程序将为桌面 Web 用户使用 CanvasKit 呈现,而为移动 Web 用户使用默认 HTML 渲染器(也有改进,但不如 CanvasKit)。
如果您尝试使用 Flutter 构建 Web 应用程序,您可能已经注意到拥有像超链接这样简单的东西并不是特别直观。 现在,至少,您可以使用Link
类创建类似于使用 HTML 时的超链接。
这实际上不是 Flutter 本身的新增功能,而是 Google 的url_launcher
包最近新增的功能。 您可以在官方 API 参考中找到Link
类的完整描述和使用示例。
文本选择得到了改进,因为现在枢轴点对应于用户开始选择文本的位置,而不是相关SelectableText
的左边缘。 此外,现在存在复制/剪切/粘贴选项并且可以正常工作。
尽管如此,文本选择仍然不是一流的,因为无法在不同的SelectableText
小部件中选择文本,并且可选文本仍然不是默认设置,但我们将讨论这一点以及 Flutter web 的其他突出缺点(缺乏 SEO 支持,首先)在本文的结论中。
Flutter 桌面更改
当我去年写关于使用 Flutter 进行 Web 和桌面开发的文章时,我主要关注的是使用 Flutter 构建 Web 应用程序,因为桌面开发仍然被认为是非常实验性的(甚至在beta
通道上也没有)。 不过现在,Flutter 桌面支持很快就会跟随 Web 支持,并且会变得稳定。
性能和稳定性得到了相当大的提升,使用鼠标和键盘操作的大型设备的总体可用性的改进使 Web 应用程序受益匪浅,这也意味着 Flutter 桌面应用程序现在更加可用。
桌面应用程序仍然缺乏工具,并且仍然存在许多非常严重的未解决的错误,因此不要尝试将它用于您的下一个桌面应用程序项目,该项目旨在公开分发。
使用 Flutter 构建的示例桌面应用程序
Flutter 桌面支持现在已经相当稳定和可用,而且它在未来肯定会变得更好,就像 Flutter 整体上已经变得更好一样,所以让我们试试看它的实际效果吧! 您可以在 GitHub 存储库上下载整个代码示例。
我们将构建的应用程序是以下非常简单的应用程序。 我们有一个侧边栏导航以及每个导航部分的一些内容项。
首先要做的是弄清楚你的依赖关系。
首先,你必须启用 Flutter 桌面开发,使用命令
flutter config --enable-${OS_NAME}-desktop
您可以将${OS_NAME}
替换为您选择的桌面操作系统, windows
、 linux
或macos
。 对于此示例,鉴于我们将使用 Ubuntu 主题,我将使用 Linux。
为每个平台构建本机应用程序还需要其他依赖项,例如在 Windows 上需要 Visual Studio 2019,在 macOS 上需要 Xcode 和 CocoaPods,您可以在 Flutter 的官方网站上找到最新的 Linux 依赖项列表。
然后创建一个 Flutter 项目,运行:
flutter create flutter_ubuntu_desktop_example
然后,我们必须通过将 yaru 添加到yaru
中的应用程序dependencies
pubspec.yaml
(在源代码树的根目录中)来获取主题本身(我们应用程序的唯一依赖项):
dependencies: yaru: ^0.0.0-dev.8 flutter: sdk: flutter
然后,让我们转到lib/main.dart
,我们的应用程序代码所在的位置。
首先,我们导入我们需要的东西。 在这种情况下,我们只需要导入常规的 Flutter Material Design 库和 Yaru 主题(我们将只在本示例中使用 light 主题,因此我们将只show
Yaru 包中的一个对象):
import 'package:flutter/material.dart'; import 'package:yaru/yaru.dart' show yaruLightTheme;
我们将在调用runApp
时直接在main
中调用MaterialApp
构造函数,而不是单独的应用程序类,这就是我们设置应用程序主题的地方,这将是 Yaru 主题,更具体地说是名为yaruLightTheme
的轻主题:
void main() => runApp(MaterialApp( theme: yaruLightTheme, home: HomePage(), ));
HomePage
将是一个StatefulWidget
,保存我们将要显示的数据,因为它是不可变的(请记住,小部件总是不可变的,可变性在StatefulWidget
的State
中处理):
class HomePage extends StatefulWidget { final dataToShow = { "First example data": [ "First string in first list item", "Second in first", "Example", "One" ], "Second example": [ "This is another example", "Check", "It", "Out", "Here's other data" ], "Third example": [ "Flutter is", "really", "awesome", "and", "it", "now", "works", "everywhere,", "this", "is", "incredible", "and", "everyone", "should", "know", "about", "it", "because", "someone", "must", "be", "missing", "out", "on", "a lot" ] }.entries.toList(); @override createState() => HomePageState(); }
HomePageState
是我们定义应用程序 UI 和行为的地方。 首先,让我们看看我们想要构建的小部件树(列表和网格项目以及间距小部件除外):
我们将使用Container
将左侧的Column
(显示控件以显示在应用程序右侧的控件)限制为一定的宽度(例如 400 像素),而右侧的GridView
应该Expanded
以填充视图。
在Row
的左侧(在Column
内), ListView
应该扩展以填充顶部按钮Row
下方的垂直空间。 在顶部的Row
中,我们还需要展开TextButton
(重置按钮)以填充左右 chevron IconButton
右侧的空间。
完成所有这些操作的结果HomePageState
以及根据用户在左侧选择的内容在右侧显示正确内容的必要逻辑如下:
class HomePageState extends State<HomePage> { int selected = 0; ScrollController _gridScrollController = ScrollController(); incrementSelected() { if (selected != widget.dataToShow.length - 1) { setState(() { selected++; }); } } decrementSelected() { if (selected != 0) { setState(() { selected--; }); } } @override Widget build(BuildContext context) { return Scaffold( body: Row( children: [ Container( color: Colors.black12, width: 400.0, child: Column( children: [ Row( children: [ IconButton( icon: Icon(Icons.chevron_left), onPressed: decrementSelected, ), IconButton( icon: Icon(Icons.chevron_right), onPressed: incrementSelected, ), Expanded( child: Center( child: TextButton( child: Text("Reset"), onPressed: () => setState(() => selected = 0), ), )) ], ), Expanded( child: ListView.builder( itemCount: widget.dataToShow.length, itemBuilder: (_, i) => ListTile( title: Text(widget.dataToShow[i].key), leading: i == selected ? Icon(Icons.check) : Icon(Icons.not_interested), onTap: () { setState(() { selected = i; }); }, ), ), ), ], )), Expanded( child: Scrollbar( isAlwaysShown: true, controller: _gridScrollController, child: GridView.builder( controller: _gridScrollController, itemCount: widget.dataToShow[selected].value.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 200.0), itemBuilder: (_, i) => Container( width: 200.0, height: 200.0, child: Padding( padding: const EdgeInsets.all(8.0), child: Card( child: Center( child: Text(widget.dataToShow[selected].value[i])), ), ), )), ), ), ], ), ); } }
我们完成了!
然后你构建你的应用程序
flutter build ${OS_NAME}
其中${OS_NAME}
是您的操作系统的名称,与您之前使用flutter config
启用 Flutter 桌面开发的名称相同。
运行您的应用程序的编译二进制文件将是
在 Linux 和
在 Windows 上,您可以运行它,您将获得我在本节开头向您展示的应用程序。
在 macOS 上,您需要在 Xcode 中打开macos/Runner.xcworkspace
,然后使用 Xcode 构建和运行您的应用程序。
其他颤振变化
还有一些变化也影响了 Flutter 的移动开发,这里只是其中的一部分。
我们中的许多人,Flutter 开发者,想要的一个功能是更好地支持 Admob 广告,现在它终于包含在官方的google_mobile_ads
包中。 另一种是autocomplete
; 它有一个Autocomplete
材料小部件,以及一个更可定制的RawAutocomplete
小部件。
我们在关于 Web 开发的部分中讨论的Link
的添加实际上适用于所有平台,尽管它的效果将最受从事 Flutter Web 项目的人的影响。
最近的 Dart 语言变化
了解影响 Flutter 应用程序开发的 Dart 语言更改非常重要。
特别是 Dart 2.12 带来了C 语言互操作性支持(Flutter 官网有详细描述并附有针对不同平台的说明); 此外,稳定的 Dart 发布通道中添加了声音null-safety
。
null-safety
Dart 最大的变化是引入了可靠的null-safety
,它得到了越来越多的第三方包以及 Google 开发的库和包的支持。
Null 安全带来了编译器优化并减少了运行时错误的机会,因此,即使现在支持它是可选的,重要的是您至少要开始了解如何使您的应用程序为 null 安全。
但目前,这可能不是您的选择,因为并非所有 Pub 包都是完全空安全的,这意味着如果您的应用程序需要其中一个包,您将无法利用零安全。
使您的应用程序null-safe
如果你曾经使用过 Kotlin,那么 Dart 的 null 安全方法对你来说会有些熟悉。 查看 Dart 的官方指南,以获得更完整的 Dart null-safety
指南。
您熟悉的所有类型( String
、 int
、 Object
、 List
、您自己的类等)现在都不可为空:它们的值永远不能为null
。
这意味着具有不可为空返回类型的函数必须始终返回一个值,否则您将收到编译错误并且您始终必须初始化不可为空的变量,除非它是一个之前被赋值的局部变量它曾经被使用过。
如果您希望变量可以为空,则需要在类型名称的末尾添加一个问号,例如在声明这样的整数时:
int? a = 1
在任何时候,您都可以将其设置为null
并且编译器不会为此哭泣。
现在,如果您有一个可以为空的值并将其用于需要不可为空值的东西怎么办? 为此,您可以简单地检查它是否为空:
void function(int? a) { if(a != null) { // a is an int here } }
如果您 100%确定变量存在且不为空,则可以使用!
运算符,如下所示:
String unSafeCode(String? s) => s!;
得出结论:我们可以用 Flutter 2 做什么?
随着 Flutter 的不断发展,我们可以用它做的事情越来越多,但是说 Flutter 可以用于任何类型的应用程序开发项目仍然是不合理的。
在移动端,你不太可能遇到 Flutter 不擅长的东西,因为它从一开始就受到支持并且已经过完善。 你需要的大多数东西都已经在那里了。
另一方面,网络和桌面还没有完全成熟。
桌面仍然有点问题,Windows 应用程序(这是桌面开发的重要组成部分)仍然需要大量工作才能看起来不错。 仅在某种程度上,Linux 和 macOS 上的情况要好一些。
网络比桌面更好。 您可以构建体面的 Web 应用程序,但您仍然主要限于单页应用程序和渐进式 Web 应用程序。 我们仍然肯定不想将它用于需要可索引性和 SEO 的以内容为中心的应用程序。
以内容为中心的应用程序可能不会那么好,因为文本选择仍然不是一流的,正如我们在有关 Flutter for web 的当前状态的部分中看到的那样。
但是,如果您需要 Flutter 应用程序的 Web 版本,Flutter for the web 可能会很好,特别是因为已经有大量与Web 兼容的包并且列表一直在增长。
其他资源
- 主题演讲(Flutter Engage),YouTube
- Flutter 2、Flutter、Medium 中的新功能
- 了解
null safety
,Dart.dev - 使用 Flutter 进行响应式 Web 和桌面开发,Smashing Magazine
- 使用 Flutter 解决常见的跨平台问题,Smashing Magazine