Flutter 3.7 正式釋出

2023-01-28 12:01:22

新的 Flutter 穩定版加入了 Material 3 更新、iOS 平臺優化及其他內容

新年伊始,由 Flutter 3.7 正式版來「打頭陣」!我們與整個 Flutter 社群成員們繼續在 Flutter 3.7 中優化了框架,包括建立自定義選單欄和層疊式選單、更好的國際化工具支援、新的偵錯工具以及其他功能和特性等。

新的穩定版裡,我們在持續改進一些特性,例如全域性文字選擇、Impeller 渲染速度、DevTools 以及一直以來都在優化的效能。讓我們一起來快速探索 Flutter 3.7 的新特性吧!

增強對 Material 3 的支援

在 Flutter 3.7 中,以下的 widget 已經進行了 Material 3 的適配:

BadgeBottomAppBarFilledFilled Tonal 按鈕、SegmentedButtonCheckboxDividerMenusDropdownMenuDrawerNavigationDrawerProgressIndicatorRadio 按鈕、SliderSnackBarTabBarTextFieldsInputDecoratorBanner

你可以直接在應用中的 ThemeData 裡設定 useMaterial3 來啟用 Material 3。只有在完整的顏色方案下才能展現出 Material 3 最完整的細節,你可以使用新的 Material 主題構建器 生成你的主題設定,也可以通過 Flutter ThemeData 構造中的 colorSchemeSeed (顏色種子) 自動生成一整套的主題:

MaterialApp(
  theme: ThemeData(
     useMaterial3: true,
    colorSchemeSeed: Colors.green,
  ),
  // …
);

若想了解 Flutter 在 Material 3 上的支援進度,你可以在 GitHub 上檢視 flutter #91605 號議題

你也可以嘗試 Material 3 範例,其中展示了所有主題的特性。

選單欄和級聯選單

Flutter 現在可以建立選單欄和級聯選單了。

在 macOS 上,你可以使用 PlatformMenuBar widget 來建立選單欄,你的選單欄將由 macOS 系統來渲染,而不是使用 Flutter。

此外,對於所有其他的平臺,你可以定義一個 Material Design 選單,它提供了級聯選單欄 (MenuBar),或者使用由 UI 介面元素觸發的 (MenuAnchor) 來建立一個級聯選單。這些選單都是完全可自定義的,其中的選單項可以是自定義的 widget,也可以使用新的選單項 widget: (MenuItemButtonSubmenuButton)。

Impeller 預覽版在穩定版渠道釋出

Flutter 團隊很高興能 在穩定版渠道上 為大家帶來 iOS 平臺的 Impeller 渲染引擎 預覽。我們認為 Impeller 的效能已經達到、甚至超越了大部分現有應用上的 Skia 渲染。在影象保真方面,Impeller 也已覆蓋了大部分除極端條件以外的應用場景。我們希望能夠在之後的穩定版本中 將 Impeller 作為 iOS 平臺的預設渲染引擎,如果你在體驗時有任何問題,請繼續在 GitHub 上提交 Impeller 的相關反饋。

儘管我們對 iOS 上 Impeller 滿足現有應用的渲染需求有足夠的自信,但仍然有部分 API 需要進行補充。你可以在 Flutter wiki 檔案 上看到目前 Impeller 的進度。使用者及開發者在使用時可能會注意到 Impeller 與 Skia 之間的渲染細節區別,這些區別可能是 BUG,當你遇到時請記得在 GitHub 上提出 Issue,幫助我們定位並修復它。

Impeller 的進展飛速離不開社群貢獻者的支援。尤其是 ColdPaleLightguoguo338JsouLiang 以及 magicianA,他們在此次釋出版本的 Impeller 的 291 次提交中有 31 次 (>12%) 是他們提交的。非常感謝他們的幫助!

我們也在 Impeller 的 Vulkan 支援 (舊裝置會回落到 OpenGL) 上有一定的進展,但是 Android Impeller 尚未準備好進行公開預覽。我們會在未來的釋出中分享更多正在積極進行的 Impeller 開發程序,包括桌面和 Web 平臺的支援。

若你感興趣,可以關注 GitHub 上的 Impeller 專案看板 來跟進開發進度。

iOS 釋出校驗

當你在構建一個釋出版本的 iOS 應用時,Flutter 會為你提供 專案設定檢查清單 來確保你的應用已經準備好釋出到 App Store。

現在 flutter build ipa 命令會校驗專案的一部分設定,並且在清單中告知你在釋出前進行更改。

開發者工具 DevTools 的更新

在本次釋出中,開發工具也帶來了新的特性和體驗優化。DevTools 的記憶體偵錯工具已經完成了一輪全面的調整。我們帶來了三個新的索引標籤:ProfileTraceDiff,它們包含了先前的所有記憶體偵錯功能,也新增了更多利於偵錯的操作。現在你可以按照類或者記憶體型別對當前的記憶體分配進行分析,可以在執行時分析哪些程式碼呼叫了哪些部分的記憶體,也可以對比兩個不同時間點的記憶體快照之間的差異來了解記憶體使用的細節。

以上的這些記憶體特性已經在 檔案 中進行了介紹,若你感興趣可以前往瞭解更多細節。

效能頁面也有一些值得注意的新功能,該頁面現在在頂部新增了 Frame Analysis (幀分析) 索引標籤,它能夠提供在 Flutter 中詳細追蹤大量消耗的某些幀和操作的一些建議。

除了以上的新功能,本次更新還有其他的問題修復和優化改進,包括檢視器 (Inspector)、網路記錄器的 CPU 記錄器的問題修復。你可以通過 Flutter DevTools 的發行注紀頁面檢視 2.17.0 - 2.20.0 版本的 發行註記 瞭解更多細節。

自定義上下文選單

從新版本開始,你可以在 Flutter 應用的任意位置建立自定義的上下文選單,也可以自定義內建的上下文選單。

舉例來說,你可以在使用者選中郵件地址時,為文字方塊預設的選擇選單新增「傳送郵件」的按鈕 (程式碼地址)。contextMenuBuilder 引數也已經新增到現有包含上下文選單的 widget 中。你可以在 contextMenuBuilder 中返回任何你想返回的 widget,也包括平臺自適應的上下文選單。

這一新特性也可以用於文字選擇以外的場景。例如,你可以為一個 Image widget 的右鍵和長按操作新增「儲存」按鈕 (程式碼地址)。你也可以使用 ContextMenuController 在應用內的任意位置展示平臺預設或者自定義的上下文選單。

若想檢視完整的範例,前往 context_menus 範例程式碼倉庫 瞭解更多。

CupertinoListSection 和 CupertinoListTile widget

Cupertino 系列 widget 迎來了兩位新成員: CupertinoListSectionCupertinoListTile,可用於展示 iOS 風格的捲動列表內容,它們是 Cupertino 版本的 ListViewListTile

滑動優化

此次版本釋出中也包含了眾多 滑動相關的問題 修復,包括觸控板的互動優化以及在滑動元件中文字選擇時的行為。

值得注意的是,macOS 的應用現在可以通過 新物理滑動特性 來體驗與其有更高匹配度的滑動體驗。

新的 AnimatedGridSliverAnimatedGrid 可以用於為新增和移除的內容展示動畫。

最後,我們 修復了 自 Flutter 遷移至健全的空安全以來的一個問題,該問題影響了所有包含 itemBuilder 引數的滑動 widget (例如 ListView)。在遷移至空安全時,itemBuilder 的型別遷移至了 IndexedWidgetBuilder,即不允許返回 null,而在以前 null 可以用來代表列表已經到了底部等。該引數現已修改為 NullableIndexedWidgetBuilder。感謝 @rrousselGit 發現並修復了這個問題!

國際化工具和檔案

Flutter 對國際化的支援已經煥然一新!我們對 gen-l10n 進行了重寫以支援下述特性:

  • 描述性的語法錯誤
  • 巢狀或多個複數、選擇和佔位的訊息內容

更多內容可以瞭解已經更新的 Flutter 應用裡的國際化 檔案。

全域性的選擇優化

SelectionArea 現在已支援鍵盤操作。你可以通過鍵盤快捷鍵 Shift+→ 等快捷鍵進行選擇。

後臺 isolate

現在 平臺通道 可以在 任意 isolate 中進行呼叫。先前平臺通道只能在主 isolate 中進行呼叫。優化後會讓外掛和混合開發呼叫 isolate 和宿主平臺程式碼更加簡單。更多內容可以閱讀 撰寫平臺程式碼 檔案以及 介紹後臺 isolate 通道 文章。

文字放大鏡

在 Android 和 iOS 上進行文字選擇時會出現的放大鏡現在也會在 Flutter 中出現了。它已經新增至了所有的文字選擇,但是你也可以通過 magnifierConfiguration 禁用或者自定義。

外掛程式碼遷移至 Swift

Apple 整將它們的程式碼遷移至 Swift,我們也希望能為開發者構建 Swift 外掛的範例和指導。quick_actions 已經從 Objective-C 遷移至了 Swift,也可以作為 Swift 外掛的最佳實踐。如果你對幫助 Flutter 遷移第一方外掛至 Swift 感興趣,請參考 wiki 中的 Swift 遷移部分

給 iOS 開發者準備的資源

我們新發布了一系列為 iOS 開發者準備的資源,包括:

廢棄 Bitcode

從 Xcode 14 開始,watchOS 和 tvOS 的應用不再需要 bitcode,並且 App Store 也不再接收帶 bitcode 的應用提交。因此,Flutter 也移除了 bitcode 的支援。

Bitcode 在 Flutter 應用中預設是關閉的,所以這也不應該會影響太多開發者的專案。但是,如果你曾經為你的專案手動啟用過 bitcode,請儘快在升級到 Xcode 14 後關閉 bitcode。你可以使用 Xcode 開啟 ios/Runner.xcworkspace 找到 Enable Bitcode 設定為 No,混合開發專案需要在宿主專案中禁用。

iOS 平臺檢視應用 BackdropFilter

我們為 iOS 原生檢視新增了可以渲染高斯模糊的特性,現在巢狀在 BackdropFilter 中的 UiKitView 可以正確的渲染高斯模糊了。

你可以檢視相應的 設計檔案 瞭解更多。

記憶體管理

此次釋出的版本對記憶體管理做了一些改進,這些改進的共同作用是減少由 GC 暫停引起的卡頓、減少由於分配速度和後臺 GC 執行緒引起的 CPU 佔用,並且降低記憶體佔用。

例如,我們擴充套件了現有手動釋放某些 dart:ui Dart 物件的本地資源的實踐。先前在 Dart VM 垃圾回收 Dart 物件前,本地資源都將被 Flutter 引擎持有。通過對使用者應用程式和我們的 benchmarks 分析,我們認為這種策略很多時候無法避免不合適的 GC 和過度使用記憶體。因此在此次更新中 Flutter 引擎新增了 API ,用於顯式釋放由 VerticesParagraphImageShader 物件持有的本地資源。

在我們遷移到此 API 的 Flutter 框架的 benchmarks 中,將 90% 的幀構建時間減少了 30% 以上,終端使用者將體驗到更流暢的動畫和更少的卡頓。

此外,Flutter 引擎 不再上報 Dart VM 中的 GPU 影象的大小。如上所述,當這些影象資源不再被需要時已由框架手動釋放,如果這時繼續按照 GPU 記憶體大小的 GC 策略上報至 Dart,會導致不必要的堆記憶體壓力並進一步觸發無效的 GC。類似的方法同樣應用到了 Flutter 引擎中,用於回收 dart:ui 原生物件的 隱式記憶體佔用

在我們的測試中,此更改省去了 widget 建立 GPU 常駐影象構建幀時的同步 GC 工作。

本次版本釋出中,Flutter 引擎在動態更新應用狀態至 Dart VM 方面有所進步。具體來說,Flutter 現在會使用 Dart VM 中 RAIL 風格API,讓 路由轉場時渲染延遲更低,即讓堆記憶體在轉場時保持增長而不是進行 GC,避免造成動畫的卡頓。目前這項改動不會帶來太大的效能優化,但未來我們會將這項改進拓展到其他方法上,消除由 GC 帶來的卡頓影響。此外,我們還修復了向 Dart VM 報告 Flutter 引擎已經閒置的 一處邏輯錯誤,也減少了 GC 帶來的卡頓。最後,在 Flutter 檢視不再展示時,也會 通知 Dart VM 進行處理,進一步優化了 Flutter 檢視未顯示時的記憶體佔用。

放棄對 macOS 10.11 到 10.13 版本的支援

我們在 Flutter 3.3 釋出的文章 中提到過,Flutter 將不再支援 macOS 的 10.11 和 10.12 版本,自上個版本釋出以來,通過進一步的 分析發現,放棄對 macOS 10.13 的支援也不會造成太大影響,帶來的收效卻是可以幫助大幅簡化程式碼庫。這意味著,使用 Flutter 3.7 以及後續版本構建的桌面端應用程式將不能再在 macOS 10.11、10.12、10.13 版本中執行,Flutter 對 macOS 的最低10點要求版本提升至 macOS Mojave 10.14。

至此,Flutter 構建的 iOS 和 macOS 應用都已經包含了 Metal 的支援,OpenGL 後端渲染引擎已經從 iOS 和 macOS 嵌入器層被移除,移除後,壓縮後的 Flutter 引擎體積降低了大約 100KB。

將 toImageSync 新增至 dart:ui 中

本次版本釋出,將 Picture.toImageSyncScene.toImageSync 方法直接加入到了 dart:ui,類似於 Picture.toImage 以及 Scene.toImage. 這樣的非同步方法,Picture.toImageSync 會直接返回一個 Picture 轉 Image 的一個控制程式碼,並在後臺非同步對 Image 進行光柵化。

當 GPU context 可用時,影象會在 GPU 中常駐,這意味著與 toImage 生成的影象相比它的繪製速度會更快。(toImage 生成的影象也可以實現 GPU 常駐,但目前還未實現)。

新的 toImageSync API 支援的例子:

  • 快速捕捉一張昂貴的柵格化圖片,以便跨多幀重複使用。
  • 應用在圖片的多路過濾器上
  • 應用在自定義著色器上

一個例子是,Flutter 框架現已使用這個 API 以優化 Android 上的頁面切換動畫的效能,幾乎減少了幀光柵化一半的時間,減少掉幀,在支援這些重新整理率的機器上動畫可以達到 90/120fps。

自定義著色器支援的改進

本次發行版包含了許多關於 Flutter 對自定義著色器片段的優化支援。Flutter SDK 現已內建了一個著色器編譯器,能夠將 pubspec.yaml 檔案中列出的 GSGL 著色器編譯為目標平臺的正確的平臺特定對應的格式。此外,自定義著色器能夠在開發階段方便的執行 hot reload。自定義著色器目前已經在 iOS 上對 Skia 以及 Impeller 都支援了。

我們為社群中分享的樣例感到印象深刻,期待能夠未來能有更多關於 Flutter 中的自定義著色器的創意。

請參閱 檔案網站上的檔案 以及 pub.dev 上的
flutter_shaders package 瞭解更多。

字型資源支援熱過載

在過去,將新的字型資源加入到 pubspec.yaml 檔案的時候會需要重新構建應用後才能檢視,不像其他資源可以直接熱過載生效,現如今,字型清單檔案的修改 (包括新增新字型) 後,也可以直接熱過載到應用中立刻可見了。

減少 iOS 裝置上動畫效果的卡頓

有兩項重要的來自社群成員 luckysmg 的貢獻,幫助減少了 iOS 裝置上動畫效果的卡頓。特別是在 iOS 手勢互動期間在主執行緒上新增一個虛擬的 CADisplayLink 以強制設定最大重新整理率。此外,鍵盤動畫也通過 CADisplayLink 設定了與 Flutter 引擎裡 animator 相同的重新整理率。由於新加入了這些變化,使用者可以在 120Hz 的 iOS 裝置上感受到更一致和流暢的動畫效果。

結語

還是那句話,如果沒有 Flutter 社群中優秀、熱情貢獻者們,Flutter 不會像現在這樣優秀,在我們未來持續進行的這段旅程中,我們希望你可以知道,沒有你們,我們無法做出這樣的優秀成績。感恩每一位貢獻者!

我們的發展勢頭依舊,請期待未來的更新!

致謝

感謝來自 CFUG 社群的 Alex、Luke、迷鹿、鑫磊 對本文的翻譯和審校。