繼 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上的「使用應用大小工具」文檔。