继 9 月 23 号发布 Flutter Windows 内测版 以后刚过几天,Flutter 官方在昨夜凌晨正式发布了 Flutter 1.22。linux
本次版本的升级又带来了新一轮的功能发布,性能改进和问题修复。恰逢移动平台新版本(iOS 14/Android 11)的发布季,这次的版本更新保证了 Flutter 应用在 Android 11 和 iOS 14 上的兼容性,面向 iOS 14,本次更新包括了对 Xcode 12,新 Icon 的更新以及 App Clips 功能的预览。对于Android 11,这次更新包括了多种屏幕适配以及软键盘动画的流畅性优化。android
距离上个版本发布刚刚两个月,这次版本的更新最为快速,但质量却依然没有降低,Github 数据显示这次更新共解决了 3,024 个 issue,合并了 197 个贡献者的 1,944 个PR,而在这些贡献者中有 114 位(58%)来自社区的支持,他们共提交了 271 个PR,贡献量最大的是 a14n,共提交了 20 个 PR。ios
除了对新平台的全力支持外,Flutter 的本次更新也迎来了不少值得分享的话题,包括社区讨论最为热烈的 Android 状态恢复,新的 Material 按钮组件以及国际化和本地化支持与热重载并用等功能。这次更新也包括了全新的导航器(Navigator),稳定版 Platform Views (支持 Google Maps 和 WebView 插件)以及高频率设备下滚动性能的优化,同时,开发工具的更新也迎来了另外一番景象。git
每次 Android/iOS 等平台推出系统的新版本时,Flutter 都会进行全面的整改来避免出现不兼容的现象。所以,iOS 14 的发布也推进了 Flutter 的新一轮更新,主要包括以下几点:github
若是你的 Flutter 应用程序须要运行在 iOS 14 系统上,咱们强烈建议你将 Flutter 版本更新到 1.22 并当即部署到 App Store 中,这样能够确保你的 iOS 14 用户得到最佳体验。web
有关 Flutter 如何适配 iOS 14 的更多信息,包括如何添加到原生应用、deep linking 等问题,能够参阅官网 iOS 14文档。咱们的目标一直是但愿开发者们能彻底脱离全部工具和 SDK 的更新而者专一于应用自己的业务逻辑,这就要求咱们须要充分支持 iOS 14 的各类新的特性。macos
本次,咱们就针对 iOS 新发布的 SF Symbols 字体作了更新支持,对 cupertino_icon 库作了一系列的更新,如今只须要将 cupertino_icon 更新到最新的 1.0 版本,就能自动将 CupertinoIcons 映射成新样式的图标,Flutter 1.22 后,CupertinoIcons 也额外提供了 900 个新图标。windows
开发者能够在 iOS 14 上 尝试使用 Flutter 的另外一个功能就是 App Clips(轻应用),这是 iOS 14 推出的一项新功能,它支持 10MB 如下轻量级应用程序的快速,免安装打开,而在Flutter 1.22 版以后的版本,咱们就能够试一下 Flutter 在 iOS 上支持的 App Clip 功能了。安全
Flutter 的这次更新也一样同步了本月发布的 Android 11。为了支持 Android 11 中引入的两个新功能,Flutter 框架层和引擎层都已作了相应的更新。首先,Flutter 如今已经支持多种全新 Android 屏幕的适配,以下图:
经过使用 MediaQuery
和 SafeArea
这两个组件,开发者就能够确保将展现的 UI 和交互式组件放置在设备显示屏的无障碍区域中。另外,目前咱们须要尽可能避免在瀑布屏边缘区域使用手势检测器,由于这些手势检测器可能会致使意外触摸。其次,显示软件键盘时的动画也已经与 Android 11 同步。性能优化
此前, Flutter 一直存在 #19279 这个问题,其中系统键盘的显示/隐藏动画与 Flutter 并不一样步,这个问题也已经在这次更新中被修复。
关于 Android 嵌入 API 的注释。去年,Flutter 1.12 推出了一套全新的 Flutter 插件 API,咱们开发了 v2 API 使开发者们可以更好的将 Flutter 嵌入到已有的原生应用中。据咱们统计,到目前为止已经有超过 80% 的 Android 插件使用了新的 Android API 了,所以,从本次发布 1.22 开后,咱们便再也不维护旧的 v1 API。
若是你仍在使用 Android v1 API,可能会致使以下问题:
同时,若是你仍然有基于 v1 Android API 的 Flutter 应用程序,它虽然可以正常运行,可是极可能会使用遇到仅支持 v2 API 的新插件,而这些插件不能被 v1 Android API 使用。
以前的版本中,Flutter 已经有了一套完备的按钮组件,但使用起来却很麻烦,Material 规范也增长了多个新样式的按钮。因此,为了使 Flutter 保持与 Material 的同步,咱们正式地宣布 Flutter 1.22 将引入全新的 “Button” 按钮。
新的 Button 组件的命名规范也与 Material Design 设计原则,以下图所示。
DartPad 上有一个很好的示例。 另外,旧的组件如FlatButton,OutlineButton,RaisedButton,ButtonBar,ButtonBarTheme也并不会被弃用,开发能够按照需求混合使用旧按钮与新按钮。
自 Flutter 发布以来,已经为应用提供了较好的国际化(i18n)和本地化(l10n)所需的核心功能的支持,而在这次的新版本中,咱们也将该功能的最佳实践归入了咱们的开发工具中,而且,在添加新的 l10n 信息时启用了热重装支持来直接更新应用程序。
若是你想了解有关 Flutter l10n 的更多信息,包括本地化消息,带有参数,日期,数字和货币的消息,请参见Flutter Internationalization 用户指南。
此外,若是你对 i18n 和 l10n 感兴趣,你可能还对那些字符串不包含在普通 ASCII 字符的字符串,例如 Unicode 和 emoji 的问题比较惯性。本次,Dart 团队也发布了 characters 软件包能够帮助开发人员处理 Unicode(扩展)字符簇。该库能够帮助开发者们解决诸如如何正确地将字符串(如“ A 🇬🇧 text in English”)缩写为前 15 个字符的问题,使用 String 类,该字符串能够缩写为 “ A 🇬🇧 text in”,它仅是 12 个用户可感知的字符。另外一方面,使用 characters 也能够生成 “A 🇬🇧 text in Eng” 的正确缩写。
此PR 使用 characters 完美的处理了这些复杂的字符,例如,当 TextField 带有最大长度 maxLength 限制时,像 👨👩👦 这样的字符如今能够正确地算做单个字符,另外,此PR,在Flutter所在的项目中,字符包都可自动在项目中使用,而无需手动添加。但愿这使得处理来自全部语言环境的各类字符串变得更加容易。有关 character 包的更多详细信息,请查看文章正确完成Dart字符串操做。
Flutter 团队一般会通过仔细考虑后才会将某些标签标记为 “production ready”,在此以前,咱们一般都会对其进行了全面测试。对于google_maps_flutter 和 webview_flutter 这两个插件底层都是使用 Platform Views 实现,从而容许将 Android 和 iOS 的原生 UI 组件嵌入在 Flutter 应用程序中。在这次的 Flutter 版本中,咱们欣然宣布,咱们已经对框架层进行了强化,彻底可以将这两个插件都声明为可投入生产(即 “production ready”)。
在 Flutter 1.22 中,咱们添加了一个替代的 Platform Views 实现,该实现修复了全部已知的键盘以及 Android 视图的可访问性问题。此外,它还适用于 19 级及以上的 Android API(之前要求 20 级)。咱们还对 iOS 上的线程进行了改进,使平台视图更高效,更可靠(而且再也不须要你将 io.flutter.embedded_views_preview 标志添加到 iOS 中 Info.plist)。
该 webview_flutter 插件支持新的 Android Platform Views 模式,但当前须要手动启用。一旦在更普遍的社区中获得更多使用,咱们将默认在未来的版本中启用它。
Google Maps 和 WebView 插件已经从 Platform Views 的改进中受益。若是你想使用平台视图在 iOS 或 Android 上嵌入本身的原生 UI 组件,能够参阅如何 Hosting native Android and iOS views in your Flutter app with Platform Views。
若是你以前在 Flutter 应用程序中使用过 Navigator,则可能已经注意到核心数据结构(用户正在浏览的页面路由堆)对你是不可见的。每次要进行管理时,须要调用 Navigator.pop() 或 Navigator.push()。例如,假设你要在主页上显示一系列组件,并容许用户点击一个组件以进入该颜色的详细信息页面,以下图。
咱们可使用下面这种方式实现这两个简单的 UI 页面,代码以下。
class ColorListScreen extends StatelessWidget { final List<Color> colors; final void Function(Color color) onTapped; ColorListScreen({this.colors, this.onTapped}); @override Widget build(BuildContext context) => Scaffold( appBar: AppBar(title: Text('Colors')), body: Column( children: [ // you can see and decide on every color in this list for (final color in colors) Expanded( child: GestureDetector( child: Container(color: color), onTap: () => onTapped(color), ), ) ], ), ); } class ColorScreen extends StatelessWidget { final Color color; const ColorScreen({this.color}); @override Widget build(BuildContext context) => Scaffold( appBar: AppBar(title: Text('Color')), body: Container(color: color), ); }
使用下面这种 Navigator 1. 0 方式,能够很是简单地实如今这两个页面之间的导航和跳转,以下所示。
class _ColorAppState extends State<ColorApp> { List<Color> _colors = [Colors.red, Colors.green, Colors.blue]; @override Widget build(BuildContext context) => MaterialApp( title: 'Color App', home: Builder( builder: (context) => ColorListScreen( colors: _colors, // the Navigator manages the list of pages itself; you can only push and pop onTapped: (color) => Navigator.push( context, MaterialPageRoute(builder: (context) => ColorScreen(color: color)), ), ), ), ); }
如上所示,只需调用 Navigator.push()
,便可在第一个页面打开第二个页面,从而在路由栈中建立两个页面的实例,可是,和在 ColorListScreen 中的 build 方法中显式地建立 Containers 列表不一样,该路由栈并不可见,所以很难管理一些特殊状况,如处理由原生嵌入提供的初始路由的 deep linking,或者来自 Web 的 URL 或来自 Android 的 intent,管理同一页面的不一样顺序之间的嵌套路由也极其困难。
Navigator 2.0 经过使页面堆栈可看法决了这些问题,甚至更多。下面这段代码是在 ColorListScreen 和 ColorScreen 之间实现跳转的另外一个版本,以下所示。
class _ColorAppState extends State<ColorApp> { Color _selectedColor; List<Color> _colors = [Colors.red, Colors.green, Colors.blue]; @override Widget build(BuildContext context) => MaterialApp( title: 'Color App', home: Navigator( // you can see and decide on every page in this list pages: [ MaterialPage( child: ColorListScreen( colors: _colors, onTapped: (color) => setState(() => _selectedColor = color), ), ), if (_selectedColor != null) MaterialPage(child: ColorScreen(color: _selectedColor)), ], onPopPage: (route, result) { if (!route.didPop(result)) return false; setState(() => _selectedColor = null); return true; }, ), ); }
这里显式地建立了一个 Navigator,并为其提供表明完整堆栈的页面列表,咱们建立一个空 _selectedColor 变量来表示还没有选择任何颜色,所以默认不显示 ColorScreen。当用户选择一种颜色时,咱们调用 setState() 更新状态,Flutter 会从新调用 build() 方法,而后就会在 ColorScreen 顶部建立一个 ColorScreen 页面。
你能够在 OnPopPage 回调函数中更新返回的状态,例如,若是用户回退,则表示他们 “取消选择” 了当前颜色,从而 _selectedColor = null 表示再也不但愿显示该页面。
Navigator 2.0 看起来像 Flutter 的其他部分,那正是她的意图,它是声明性的,与 Navigator 1.0 势在必行,这个想法是要在导航和Flutter 的其他部分之间统一模型,同时解决许多问题并添加功能。实际上,这个小例子几乎还不涉及 Navigator 2.0 的内容。有关详细信息,推荐阅读 Declarative navigation and routing in Flutter。
另外,Navigator 1.0 依然能够继续使用,短时间内也不会失效,若是你已经喜欢这种路由模式,彻底能够继续使用它。可是,若是你尝试使用 Navigator 2.0,咱们认为你会喜欢的。
在这次的新版本中还可以试用一些新功能,如对Android的状态还原的 支持。这是咱们在 Github 上最受欢迎的功能之一,拥有 217 个点赞!
考虑到读者们可能不熟悉状态还原这个需求。移动操做系统可能会杀死后台的应用程序,以回收前台应用程序的资源。发生这种状况时,操做系统会通知该应用已经被终止,这样开发者就能够快速保存当前 UI 状态,以便在用户再次回到该应用时能够将其恢复。若是该功能完善,就能够为用户提供无缝的体验了,同时也能够更好地利用设备的资源。目前,Flutter 还并不支持状态还原,若是没有框架层的支持,也很难自行地进行状态地还原,所以,在 Flutter 1.22 中咱们也宣布推出该功能的基础实现,完善的话还须要进行优化。
例如,下面是一个用于恢复默认 Flutter Counter 应用状态的简单示例,代码以下。
class CounterState extends State<RestorableCounter> with RestorationMixin { @override String get restorationId => widget.restorationId; RestorableInt _counter = RestorableInt(0); @override void restoreState(RestorationBucket oldBucket) => registerForRestoration(_counter, 'count'); void _incrementCounter() => setState(() => _counter.value++); @override Widget build(BuildContext context) => Scaffold( body: Center(child: Text('${_counter.value}')), floatingActionButton: FloatingActionButton(onPressed: _incrementCounter), ); }
简要地说,每一个组件都有一个存储桶(storage bucket),RestorationMixin 使用惟一的 ID 向其注册。经过使用一种RestorableProperty 类型(如这里的 RestorableInt)来存储特定于 UI 数据,并使用状态恢复功能注册该数据,该数据将在 Android 终止该应用程序以前自动保存,并在其再正常运行时进行恢复。就是这样,Restoration* 能够保存任何类型的数据,如RestorableInt,RestorableString 和 RestorableTextEditingController(等等)都将能够被恢复。
若是系统内置没有涵盖你要还原的数据类型,也能够经过 RestorableProperty<T> 建立本身的类型 。
为了实现状态恢复的自动测试,咱们也向 WidgetTester 增长了全新的 restartAndRestore API。若是想要手动测试,最简单的方法就是在 Android 设备上打开已经启用状态恢复的 Flutter 应用,在 Android 开发人员设置中启用“不要保留活动”(以下图),而后运行 Flutter 应用,将其置于后台,以后再返回。此时,Android 系统就会先终止再恢复你的应用程序了,你能够查看一切是否按预期工做。
虽然咱们已经推出了状态恢复的预览版,但还有不少其余的工做要作。例如,状态恢复不只须要适用于 Android,iOS 应用程序也应当及时同步。此外,咱们也正着手优化本身的内置组件,以在恢复过程当中默认保持其状态。咱们已经在 ListView 和 SingleChildScrollView(记住用户的滚动位置)和 TextFields (恢复他们输入的文本)类中提供了该功能支持,咱们也正计划将其扩展到其余组件中。
然而,因为 navigation(1.0 或 2.0)的缘由,该功能目前也才出预览版,,也就是说,你的用户还不能体验该功能,咱们即将会在 Beta 中发布,并在 Flutter 的下一个稳定版本中正式发布。
因为存在输入和显示频率不一样步的状况,Flutter 团队也与 Google 内核部门合做,极大地提升了页面滚动性能。例如,Pixel 4 输入的运行频率为 120hz,而显示屏的运行频率为 90hz,滚动时,这种不匹配会致使性能降低。使用新的 resamplingEnabled 标志,你就能够解决此问题,以下:
void main() { GestureBinding.instance.resamplingEnabled = true; run(MyApp()); }
根据所涉及的频率差别,启用此标志可使滚动时的颤动减小到 97%,当咱们肯定这已是最好的体验时,咱们已经计划在之后的版本中默认启用此功能。
做为Flutter 1.22的一部分发布的工具包括一个新的输出大小分析实用程序。此工具可帮助诊断Flutter,您的应用大小细分是否会随着时间变化。
您能够经过将--analyze-size标志传递给如下任何命令来使用该工具收集分析所需的数据,以下所示。
在构建Flutter输出工件时使用此标志将打印工件尺寸和组成的摘要。这包括本机代码,资产,甚至是已编译Dart代码的程序包级细分。
此摘要有助于快速识别应用程序的程序包大小用法中的热点。此外,收集到的数据还能够做为JSON文件使用,供Dart DevTools使用,它使您能够按照flutter.dev上的说明进一步浏览应用程序的内容,查明大小问题并查看两个不一样JSON文件之间的更改。加载JSON文件后,您将拥有一个界面,该界面为您提供应用大小的树状图。
有关您可使用“应用大小”工具执行的操做的更多详细信息,请阅读flutter.dev上的“使用应用大小工具”文档。