【文章翻譯】Flutter Performance Updates in the first half of 2020

【文章內容使用 Gemini 1.5 Pro 自動產生】

Flutter 在 2020 年上半年的效能更新

作者:Yuqian LiShams Zakhour

速度是 Flutter 的關鍵支柱。本文重點介紹 Flutter 社群成員在 2020 年上半年實施的效能改進。

如果您在 2020 年 6 月之後幫助過效能改進,我們將在未來的文章中涵蓋這些內容。我們希望與您分享這些內容,希望 Flutter 社群受到鼓舞,一起參與改進 Flutter 效能的努力!

量化改善

貢獻者: alexmarkovsstricklmkustermannaskeksa-googlermacnak-googlemralephcrelier

[應用程式大小] 透過字體子集處理,Gallery 應用程式縮減了 100KB 的大小

貢獻者: dnfieldjonahwilliamszandersojmagmanblasten

  • PR 1482849737498425022451808
  • Flutter Gallery 應用程式縮减了 100KB 的大小 (大小縮減)
  • 修復了「搖樹 Material 圖示 16311」,「將 hb-subset 作為引擎組建的一部分進行組建並上傳到雲端儲存 43642」,「在 flutter_tools 中使用 font-subset 並搖樹圖示字體 43644」,「為所有平台連接字體子集處理 49730“)。

[速度、記憶體] dart2js 工具的運行速度提升了 9 倍,記憶體減少了 99% 以上

貢獻者: rmacnak-googlea-siva

[速度] 從 OpenGL 切換到 Metal,iOS 平均渲染速度提升了 50%

貢獻者: chinmaygardednfieldjason-simmonscbrackenamirhliyuqian

[速度] 由於著色器編譯,最差畫面渲染時間提升了 2 到 5 倍

貢獻者: liyuqianjonahwilliamschinmaygardeiskakaushikzandersosfshaza2filiphacoutts

  • PR 173005385917601178615874356638443012142
  • Android 的 flutter_gallery__transition_perf 最差畫面渲染時間 worst_frame_rasterizer_time_millis 提升了 2.25 倍 (從 90ms 到 40ms)
  • iOS 的 flutter_gallery_ios32__transition_perf 最差畫面渲染時間 worst_frame_rasterizer_time_millis 提升了 5 倍 (從 300ms 到 60ms)
  • 一些實際應用程式的速度提升了 3.75 倍 (從 229ms 到 61ms)
  • 修復了「基於 SkSL 的著色器預熱 53607」、「三次貝茲曲線回歸 35142」和「儲存」一些客戶。

[速度] 滑鼠點擊測試速度提升了 15.8 倍

貢獻者: dkwingsmtyjbanovgoderbauergspencergoog

  • PR 59803 59883
  • bench_mouse_region_grid_scroll 的畫面持續時間 (網頁) 提升了 15.8 倍 (從 79ms 到 5ms)。
  • 修復了「訂閱 MouseRegion 事件時效能低下 41194

[速度] 平台訊息回應處理速度提升了 13.9 倍

貢獻者: zljj0818jason-simmonsliyuqian

  • PR 1883818945,兩者都受 18808 啟發
  • 修復了「載入大型圖片時,Image.asset 會阻塞 UI 線程 58572
  • PlatformMessageResponseDartComplete 速度提升了 13.9 倍 (從 9164us 到 660us)

[速度] 使用不透明路由時,畫面構建時間提升了 20% 到 37%

貢獻者: goderbauerdnfield

  • PR 48900
  • flutter_gallery_ios32__transition_perf 的平均畫面構建時間 average_frame_build_time_millis 提升了 20% (從 7.38ms 到 6.13ms)
  • flutter_gallery_ios32__transition_perf 的第 99 個百分位數畫面構建時間 99th_percentile_frame_build_time_millis 提升了 37% (從 50.45ms 到 36.63ms)
  • 修復了「在推入覆蓋/導航器不透明內容時,優化不必要的重建 45797

[速度] 使用 ImageFiltered 而不是 BackdropFilter,模糊效果速度提升了 4.8 倍

貢獻者: flarhixieyjbanovliyuqian

[速度] 使用 Flutter 網頁捲軸大型靜態內容時,速度提升了 14 倍

貢獻者: yjbanovferhatb

  • PR 17621
  • text_canvas_cached_layout.html.layout.average 速度提升了 14.01 倍 (從 463.74ms 到 33.10ms)
  • 修復了「使用 Flutter Web 捲軸大型靜態內容時卡頓 42987」和「在 Web 上繪製畫布邊界外的文字很昂貴 48516

[速度] Flutter 引擎 Shell 初始化速度提升了 6.8 倍

貢獻者: scutlightjason-simmonsgaaclarkeliyuqian

[速度] 限制條件相同時,跳過調用構建器,速度提升了 5 倍

貢獻者: yjbanovhixie

  • PR 55414
  • 使用 Flokk 客戶端應用程式捲軸聯絡人列表時,速度提升了 5 倍(從大約 10FPS 到大約 50FPS)。
  • 修復了「LayoutBuilder 應該快取限制條件,如果沒有改變就不應該重新建立 6469

[速度] 網頁上的陰影和動畫速度提升了 2 倍

貢獻者: ferhatbmdebbaryjbanov

貢獻者: ferhatbclocksmithrami-a

[速度] 在 Fuchsia 中啟用柵格快取後,平均畫面渲染時間提升了 2.8 倍

貢獻者: drevemanliyuqianchinmaygardearbreng

  • PR 17753
  • frame_rasterizer_times_avg 速度提升了 2.8 倍(從大約 10ms 到大約 3.5ms)
  • 修復了「由於柵格快取被停用,Fuchsia 上的效能低下 54950

[速度] 透過排除無效的動畫圖片,第 90 個百分位數畫面構建時間提升了 1.85 倍

貢獻者: dnfieldliyuqiantvolkertdigiter

  • PR 5085150842
  • animated_placeholder_perf 的第 90 個百分位數畫面構建時間提升了 1.85 倍 (從 3.148ms 到 1.699ms)
  • 修復了「當長列表中的卡片內部使用列表構建器時,flutter 的畫面速率很低 (平均 20 到 35fps) 35592

[速度] Google 智能顯示器 P10 的 FPS 提升了 2 倍

貢獻者: chinmaygardednicoaracbrackenjason-simmonsasakhartrdaum

  • PR 15980
  • 透過讓嵌入器可以調整線程優先順序,速度提升了 2 倍(從 30fps 到 60fps)。
  • 修復了問題 49551

[速度] 透過增強子元素快取,ImageFilterLayer 的速度提升了 2.45 倍

貢獻者: flarliyuqian

  • PR 171755490358277
  • ImageFiltered Transform 的平均畫面渲染時間 average_frame_rasterizer_time_millis 提升了 2.45 倍 (從 18.41ms 到 7.53ms)
  • 還修復了「由於 OpacityLayer 柵格快取未命中,轉場效能略有下降 52864

[速度] 大型 dart2js 編譯速度提升了 15%

貢獻者: rmacnak-googlea-siva

[速度] 大型應用程式的 AOT 編譯時間提升了 1.8 倍

貢獻者: alexmarkovmralephsigurdmcskau-g

[速度] Dart RegExp 在 AOT 模式下的匹配速度提升了 5 到 13 倍

貢獻者: mralephrmacnak-google

[速度] Dart UTF8 解碼速度提升了 5 倍

貢獻者: askeksa-googlemkustermannrakudrama

  • 提交 cf6f89e35ca378df4afa2fd4
  • X64JIT TwoByteString 基準測試速度提升了 5.02 倍。
  • 修復了「快速路徑 Uint8List 到 _OneByteString 轉換 41703」、「直接將 UTF-8 結果寫入字串 41704

[速度] async/sync* 函數的速度提升了 35% 到 65%

貢獻者: cskau-gmkustermannmraleph

  • 提交 e29407fdca1f7ed169ca94ad
  • 在 {dart,dart-aot}-{ia32,x64,armv7hf,armv8} 上,Calls.IterableManualIterablePolymorphicManyYields 的速度提升了 35% 到 65%。
  • 修復了「改進 VM 上 sync* 程式碼的效能 37753」、「在 VM 中生成 sync-yield 函數時,評估使用 IndirectGoto 37754

[速度] 使用未裝箱的參數和欄位,時間最多減少了 31.82%

貢獻者: mkustermannmkustermann

  • 提交 9eb531b95149e076e79eb531
  • matrix_utils_transform_rect_affine 迭代時間最多減少了 31.82%。
  • 修復了「Dart VM 中的真實未裝箱欄位 40004

[速度] Dart Pointer<Int8,Int64,etc> 在 AOT 中的載入/儲存速度提升了 20% 到 25%

貢獻者: dcharkesmkustermann

  • 提交 408123
  • 在 x64 上,AOT 中的 Pointer&lt;Int8,Int64,etc&gt; 載入/儲存速度提升了 20% 到 25%。
  • 幫助「儲存和載入應該支援未裝箱的索引 (在 AOT 中) 39432

其他改進

[應用程式大小、能源、記憶體、速度] 將 A/B 測試模式加入到本機 Devicelab 執行器

貢獻者: yjbanovflarferhatbchristopherfujinoliyuqian

[應用程式大小] 修復了「常見問題解答中 iOS 應用程式大小不正確」

貢獻者: [jmagman](https://github.com/

【文章翻譯】Are you happy with Flutter? — Q4 2020 user survey results

【文章內容使用 Gemini 1.5 Pro 自動產生】

您對 Flutter 感到滿意嗎? - 2020 年第 4 季使用者調查結果

Google 的 Flutter 團隊自 2018 年以來便持續進行每季一次的調查,以收集您的意見回饋 自 2018 年以來。上一季度的調查於 2020 年 11 月 23 日發布,並持續進行了 8 天。團隊收集了 8,285 份回饋,我們想與您分享這些結果!儘管這次調查的長度只有我們平常調查的一半,但我們還是像往常一樣從您的答案中學到了很多。我們非常感謝您撥冗提供寶貴的意見回饋。

使用者滿意度

總體而言,92% 的受訪者對 Flutter 有些滿意非常滿意。我們很高興能夠持續維持高滿意度。

92% 的使用者對 Flutter 感到滿意,其中 51% 非常滿意。

從圖表中可以看出,我們也學到了以下資訊:

  • 91% 的受訪者表示 Flutter 很適合他們的專案。
  • 90% 的受訪者承诺并希望在下一个项目中使用 Flutter。
  • 58% 的受访者表示 Flutter 对其公司的成功至关重要。

這些結果表明 Flutter 繼續蓬勃發展,並成為開發者工具箱的重要組成部分。

90% 的使用者希望在下個專案中使用 Flutter。

但是,我們注意到整體滿意度自上一季調查以來略有下降,從 94% 降至 92%。為了了解為什麼您對 Flutter 的各個方面並未完全滿意,團隊仔細審閱了開放式意見回饋。感謝您的回饋,團隊意識到了一些主要問題,並將投資於高優先順序項目。例如,我們計劃 改進程式碼完成,因為我們發現這是 IDE 相關問題中最常見的挫折來源。

難點

這次,我們採用了不同的方法來了解 Flutter 的難點。我們想了解什麼阻礙了您更多地使用 Flutter。當我們詢問「如果不是因為以下原因,我會更多地使用 Flutter」,26% 的受訪者選擇了 缺乏關鍵函式庫 作為原因。作為回應,我們正與 BaseflowInvertaseCodemagic 合作,以提高我們第一方套件和外掛的品質。雖然團隊正在努力改進先前研究中識別出的關鍵函式庫,但我們也在使用標籤 would be a good package 監控問題追蹤器中的特定需求,以便任何人都可以找出生態系統中缺少什麼,並為其做出貢獻。

由於 Flutter 相容套件的數量超過 15,000 個,並且 Flutter 團隊無法擴展規模來處理可能被認為是關鍵的數十甚至數百個套件,因此我們為社群提供了一系列機制來 評分套件。我們提供這些機制,以便搜尋結果能產生高品質的結果,包括讚好、靜態分析和 Flutter Favorite 認證,該認證由一群 Flutter 社群志願者頒發。這樣做不僅鼓勵您貢獻更多套件來涵蓋關鍵方面,而且套件的品質也會隨著時間推移而提升,因為套件作者會因其工作而獲得更好的分數和認可。我們將繼續投資於這個領域,以便生態系統能夠自給自足,並持續自然地提高品質。

26% 的使用者需要更多函式庫。此結果不包括 19% 表示我目前使用 Flutter 足夠的使用者。

為了讓那些從事 Flutter 之外專案的人(19%)更容易,我們將繼續投資於 add-to-app,這有助於您將 Flutter 模組整合到現有的 Android 或 iOS 應用程式中。您可以在 GitHub 議題 72009 上追蹤相關功能的進度。

核心框架中缺少的功能 被 18% 的受訪者選中。在我們仔細查看開放式意見回饋後,發現回饋集中在 Flutter 框架中包含的功能與套件提供的功能之間的界線上。雖然有些人希望框架包含更多超出使用者介面 API 的功能是可以理解的,但我們的重點是我們最擅長的 - 提供出色的 UI 框架。但是,我們想為您提供所需的功能,因此我們與社群貢獻者合作,透過套件和外掛提供特定領域和特定平台的功能。如前所述,我們投資了工具和計畫來認可高品質的套件,並幫助您在生態系統中發現它們。我們歡迎您提供回饋意見和想法,以進一步增強我們的套件生態系統。

線上社群

在這次調查中,團隊想知道您在哪裡與其他 Flutter 開發人員交流資訊或想法。雖然 21% 的受訪者表示他們沒有參與任何線上社群,但我們發現 Stack Overflow 是 Flutter 開發人員相互聯繫的最受歡迎的社群。38% 的受訪者選擇 Stack Overflow 作為他們最活躍的線上社群。

Stack Overflow 是 38% 的受訪者的主要線上社群。此圖表省略了 21% 沒有參與任何線上社群的使用者。

受訪者最活躍的地方不同,他們對社群有用性的評分也不同。例如,我們詢問「您在多大程度上同意或不同意以下關於您在上一個問題中選擇的線上社群的陳述?」對於回應 *”當我在社群中向其他 Flutter 使用者提問時,我能夠得到答案”*,在 Stack Overflow 上活躍的 75% 受訪者同意他們在 Stack Overflow 上提問時能夠得到答案。此外,61% 主要活躍在即時通訊服務上的受訪者表示他們也得到了答案。

受訪者被要求評估他們對 *"當我問…” 的問題時,我能夠得到答案"* 這個陳述的同意程度,結果因他們活躍的社群而異。

這是我們第一次評估使用者在線上的分佈情況以及他們對各種線上社群的看法。建立一個有用的和蓬勃發展的社群對 Flutter 的成功至關重要。我們與您分享這些結果,希望幫助提供者和尋求者都能更好地利用這些線上溝通管道。Flutter 團隊將監控這些資料,以指導我們的支援和教育工作,並確保您能夠尋求幫助並保持聯繫。

Flutter 活動

每年都会由当地社区小组和 Flutter 贡献者组织许多面向 Flutter 社区的线下和线上活动,约 90% 参加活动的受访者表示他们在活动中了解了 Flutter 的新知识,并认为这些信息很有帮助。虽然这些活动是有效的学习机会,但通过我们的研究,我们现在知道大多数受访者 (61%) 在 2020 年并不知道这些活动。即使我们没有像 2019 年那样举办 Flutter Interact 这样的大型活动来宣传这些活动,但这个百分比仍然高于我们的预期。因此,在计划 2021 年 3 月 3 日的 Flutter Engage 活动 时,我们将与区域 Google 团队合作,帮助宣传这些活动。我们也正在与当地 Flutter 小组密切合作,以确保我们的活动内容能够传达给更多 Flutter 社区成员。每个人都欢迎参加这个免费的线上活动。

61% 的在 2020 年没有参加任何活动 (线上和线下) 的使用者不知道有这些活動。
85% 或以上的使用者從各種 Flutter 活動中學到了新東西。

接下來會發生什麼事?

下一項調查將於 2 月底左右在 flutter.devFlutteDev (和 Flutter IDE Plugin 中宣布。您也可以透過 註冊 來參與其他研究,以參與即將到來的 UX 研究。

請繼續分享您的想法,因為團隊正在尋找重要問題的答案。

再次感謝所有參與本次調查並提供宝貴意見回饋的人。感謝您的時間和努力!

Flutter Engage 期待與您相見!


您對 Flutter 感到滿意嗎? - 2020 年第 4 季使用者調查結果 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

【文章翻譯】Join us for

【文章內容使用 Gemini 1.5 Pro 自動產生】

加入我們,一起參與 #30DaysOfFlutter

30 天 Flutter 活動的標語

新年快樂各位!現在正是學習新事物的好時機!您是否在假期裡想過要開發一個應用程式?如果是的話,我們有一個絕佳的機會等著您!從 2 月 1 日開始,在我們於 3 月 3 日舉行的盛大 活動 之前,加入我們參與 #30DaysOfFlutter,開啟您的學習旅程,並結識社群中的 Flutter 專家。無論您是正在開發您的第一個 Flutter 應用程式,還是在尋找提升 Flutter 技能的方法,我們都準備了精心策劃的內容、程式碼實驗室和示範!

Flutter 是 Google 的開源 UI 工具包,用於從單一程式碼庫建立精美的原生編譯應用程式,適用於行動、網頁和桌面。它是成長最快速、需求量最大的跨平台框架之一,被全球的自由接案開發員和大型組織使用。Flutter 使用 Dart 語言,因此對於熟悉面向物件語言的許多人來說,它會感覺很自然。

跳進來吧,水很溫和!

除了精心策劃的內容,我們還將舉辦四場現場 問我任何問題 (#AMA) 討論會,您可以在其中與 Google 的 Flutter 團隊和社群成員見面。您也可以加入我們在 FlutterDev Discord 頻道 上,在那裡您可以結識社群中的其他成員,提出和回答問題,甚至還可以交到新的 Flutter 朋友!

聽起來很令人興奮嗎?請訪問 30 天 Flutter 網站 了解更多資訊,並註冊加入。註冊截止日期為 2021 年 1 月 31 日太平洋時間晚上 11:30。

#30DaysOfFlutter 議程

您在本月的 Flutter 學習旅程將如下所示:

第 1 週

收到精心策劃的內容到您的收件匣。在 Discord 上結識其他 Flutter 開發人員。參加 2 月 1 日的啟動網路研討會。

第 2 週

收到更多內容。開始開發您的第一個 Flutter 應用程式。加入網路研討會並提出您的問題。

第 3 週

繼續開發您的應用程式,並參加第 3 場網路研討會,提出您的問題。

第 4 週

完成您的專案,學習如何與 Flutter 社群分享您的作品。

您是否準備好學習世界上最熱門的開發者技能之一?

註冊 加入這個旅程,並務必在 Twitter 上關注 FlutterDev (@FlutterDev),以獲得有關 #30DaysOfFlutter 的最新更新。


加入我們,一起參與 #30DaysOfFlutter 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

http://creativecommons.org/licenses/by/4.0/

【文章翻譯】Dart and the performance benefits of sound types

【文章內容使用 Gemini 1.5 Pro 自動翻譯產生】

Dart 與可靠類型帶來的效能優勢

3 screenshots of machine code: much code → less code → 3 lines of code.
從 Dart 1.24、2.0 和 2.12(從左到右)中的同一個 Dart 方法生成的程式碼變得更小。要了解原因(並查看實際生成的程式碼),請继续阅读。

在過去的幾年中,我們強化了 Dart 的類型系統。最初的 Dart 語言(Dart 1)有一個不可靠的、可選的類型系統(類似於類型的 JavaScript 方言,例如微軟的 TypeScript 或 Facebook 的 Flow)。Dart 2 引入了一個更嚴格的、可靠的類型系統。在過去的兩年中,我們一直在努力透過 可靠的空安全 進一步擴展類型系統。

雖然可靠的類型系統為開發人員提供了更大的信心,但它也使我們的編譯器能夠安全地使用類型來優化生成的程式碼。有了可靠性,我們的工具透過結合靜態和(必要時)運行時檢查來保證類型的正確性。沒有可靠性,類型檢查只能進行到一定程度,並且靜態類型在運行時可能不正確。

在實踐中,可靠性允許我們的編譯器生成更小、更快的程式碼,尤其是在提前 (AOT) 設定中,我們將預編譯的原生程式碼傳送給客戶端。

範例

以下範例方法演示了可靠類型如何對相對簡單的程式碼產生巨大影響:

1
2
3
int getAge(Animal a) {
return a.age;
}

在我們最後一個穩定的 Dart 1 版本 (1.24.3) 中,這個方法映射到 26 個原生 x64 指令——這還是在檢測和設定檔引導的優化之後,這會減慢初始運行時啟動速度。在 Dart 2.12 中使用可靠的空安全,此程式碼僅映射到 3 個指令,而無需任何設定檔引導的優化。

Dart 編譯為 ARM32/64 和 x86/x64 架構。在以下範例中,我們使用 x64,但在其他目標上的結果類似。

範例方法的完整 Dart 程式碼和上下文顯示在本文末尾,但以下是重點:

  • Animal 類包含一個類型為 int 的欄位 age
  • Animal 有幾個子類別(CatDogSnakeHamster)。
  • 上述方法在運行時在許多這些類型上被調用。

Dart 物件佈局

當編譯為原生 (x64) 程式碼時,Dart 類 Animal 有一個簡單的佈局:

8 bytes of object header, followed by 8 bytes of `age` field, followed by additional subclass fields.

前 8 個位元組是一個標題,提供具體化的類型資訊(即物件的運行時類型)。第二個 8 個位元組包含 age 欄位。所有子類別都保留(並可能添加到)此結構:任何額外的欄位都佈局在後面,保留基本類型的結構。給定 Animal(或任何子類別)的實例,getAge 方法應從 8 位元組偏移量載入欄位並返回它。

Dart 1:不可靠的類型

然而,在 Dart 1 中,靜態類型並不可靠,並且在編譯期間實際上被忽略。在運行時,我們不能假設靜態類型是正確的(因此,佈局也是預期的)。對 age 的存取可能是對不同偏移量處的欄位、對觸發更多可執行程式碼的 getter 或對不存在的欄位(觸發可捕獲的運行時錯誤)的存取。

Dart 1 被設計為依賴於客戶端設備上的即時編譯器和虛擬機器,該編譯器使用運行時類型資訊來優化程式碼。在這種方案中,我們實際上編譯了每個方法兩次:第一次是為了收集資訊,第二次(對於熱點方法)是根據觀察到的運行時行為生成更優化的程式碼。

Dart 1:第一次編譯

getAge 的第一次編譯在 x64 上產生了以下 47 個指令:

2 columns full of assembly code.

請注意,此程式碼已檢測以確定運行時會發生什麼。它對傳遞的物件沒有任何假設,並且有效地執行等效於雜湊表查找以正確找到欄位、執行 getter 或拋出錯誤。

Dart 1:第二次編譯

在這種情況下,程式碼會被重複調用,並觸發第二次優化編譯,生成以下 26 個指令:

2 columns of assembly code (but less than before). Most of the code is blue (prologue/epilogue) or red (various checks).

這個優化的程式碼仍然很大。它基於設定檔資訊,發現該方法僅在 CatHamsterDog 的實例上被調用,並根據未來也將如此的假設進行了優化。

藍色 程式碼是方法的序言和結尾(用於設置和恢復堆疊框架)。紅色 程式碼檢查預期的情況——實例非空且屬於先前看到的類型之一——並為其他情況調用慢路徑。粗體 程式碼是載入欄位的實際工作。

如果未來的行為與過去不同,優化的程式碼實際上可能會更慢:如果在新的實例(例如 Snake)上調用 getAge,程式碼將執行額外的檢查,但仍然會進入慢路徑。

Dart 1 生成的程式碼的問題

上面的生成的程式碼在結構上與 Chrome 中的 JavaScript 引擎 V8 在給定或多或少等效的 JavaScript/TypeScript/Flow 程序時生成的程式碼非常相似。雖然這種方法(和相應的生成的程式碼)可以在許多情況下提供良好的效能,但當我們開始(尤其是使用 Flutter)面向更廣泛的客戶端平台(包括對大小和記憶體佔用更敏感的行動設備)時,它就不再適合了:

  • 首先,客戶端編譯的成本增加了 Dart 應用程式的整體佔用空間。
  • 其次,兩階段推測編譯的成本對應用程式啟動不利。
  • 第三,iOS 上不允許即時編譯:我們至少需要針對某些目標的替代策略。

我們轉而採用提前編譯方法,但使用 Dart 1 會導致程式碼品質差很多。即使使用複雜的、全程序分析,我們也無法始終在編譯時確定類型資訊,尤其是在應用程式變得更大時。此外,當整個應用程式都被預編譯時,推測的成本(上面的紅色程式碼)變得過高。

Dart 2:可靠的類型

在 Dart 2 中,我們引入了可靠性,這使我們能夠安全地根據類型資訊編譯程式碼,並減少了對設定檔以獲得效能的依賴。使用 Dart 2,在單個提前編譯上,我們在 x64 上生成 10 個指令:

Much less code, but still there’s some blue code (prologue/epilogue) and red code (null checks).

此程式碼仍然執行空檢查(紅色),如果發現空則調用輔助方法。

Dart 2.12:可靠的空安全

有了可靠的空安全,類型系統更加豐富,我們的編譯器可以利用這一點。編譯器可以安全地依賴於(現在的)非空類型,並消除上面的紅色程式碼。在 Dart 2.12 beta 中,我們減少生成了 3 個指令:

Some blue code (prologue/epilogue) but no red code! No null checks needed!

事實上,隨著程式碼變得更簡單,我們也能够簡化序言和結尾。在我們即將發布的穩定版本中,我們將只為範例方法生成 3 個指令:

Even less blue code (prologue/epilogue) than before.

有了可靠的空安全,我們可以將此方法的生成的程式碼減少到其本質:欄位載入。在實踐中,對此方法的調用將始終被內聯,因為編譯器現在可以輕鬆地看到內聯是效能和程式碼大小的雙贏。不再需要運行時檢查和補償程式碼:更多的繁重工作在編譯時完成。我們不再需要客戶端編譯的啟動和記憶體開銷。因此,我們的用戶可以獲得更小、更快的程式碼。

試試看!

我們鼓勵您嘗試 空安全。它在 Dart 2.12 中可用,現在在我們的 beta 頻道中。一旦您的上游相依項被遷移,您就可以遷移您自己的套件和應用程式。正如這裡的範例所示,您可能不需要做太多更改。

請記住,要獲得空安全的效能優勢,您需要一個完全遷移的應用程式。一旦您的應用程式完全遷移,我們的編譯器將自動利用空安全來生成更好、更小的程式碼。

附註:程式碼

這是完整的 Dart 程式碼,我編譯它生成了本文中的所有程式碼。雖然這裡的範例是人為設計的,但模式(類別層次結構中的欄位)相當常見。


Dart 與可靠類型帶來的效能優勢 最初發佈在 Dart 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

http://creativecommons.org/licenses/by/4.0/

【文章翻譯】Providing operating system compatibility on a large scale

【文章內容使用 Gemini 1.5 Pro 自動產生】

大規模提供作業系統相容性

使用 Flutter 編寫的應用程式可以在 Android、iOS、網頁和桌面作業系統上運行。由於我們在支援多個平台方面的投入,Flutter 應用程式可以在任何具有應用程式編寫時所用作業系統版本(或更高版本)的設備上運行,而無需進行任何修改,這擴展了 Flutter 的跨平台吸引力。

在本文中,Flutter 的核心開發團隊(我們)想要分享我們為何投資於支援多個平台,我們如何才能繼續支援多個平台,以及在需要新增對新平台的支援或停止支援舊平台時,我們如何做出決定。

展望未來:Android 和 iOS 的版本

Flutter 致力於為 Android 和 iOS 的最新功能提供完全支援。我們始終監控著 Apple 和 Google 發布的有關其平台 API、工具使用和授權條款變更的定期指南。對新作業系統版本的支援是我們定期產品規劃的一部分,我們會盡量將我們的版本與 Google 和 Apple 的最新指南保持一致,以確保我們的穩定版本始終與兩者的最新指南相容。

我們承諾對您提供多個平台和 Android 和 iOS 的軟體版本(例如 Android 11 或 iOS 14)以及其小版本更新的支援。對於目標平台的主要修訂,我們會在了解到所需內容後立即開始開發必要的功能和更改。這通常發生在春季後期,Google 公佈了其即將發佈的 Android 版本的計劃,而 Apple 在其全球開發者大會 (WWDC) 上發布了最新公告。在發布公告之後,我們會評估 Flutter 程式碼庫中必要的更改,例如由於可能已棄用的 API 導致的引擎更改,工具更改如何影響 flutter 工具和開發人員體驗,以及兩者的設計語言更改。我們還會評估工作範圍,預期這兩個平台都将在秋季初提供給客戶。

我們會針對 Android 和 iOS 的測試版進行這項工作,並會透過我們的 master、dev 和 beta 頻道定期提供這些更改。master 頻道的版本是持續性的。我們計劃每週發佈兩到三個 dev 頻道版本(針對 Google 內部應用程式和測試套件進行測試的 master 頻道版本)。beta 頻道通常在每月的第一週發佈。我們會使用 GitHub 問題追蹤我們的進度。若要查看問題清單,您可以根據 platform-iosplatform-android 搜尋。

將我們的穩定版本發佈時間表與 iOS 保持一致尤其具有挑戰性,因為從新聞發佈到我們獲得有關作業系統更改的指南之間的時間通常非常短。我們會透過在秋季規劃我們的其中一個版本,並準備好針對 iOS 或甚至 Android 上出現的任何阻礙性錯誤對我們目前的穩定版本進行熱修復來減輕這一挑戰。(這發生在 Flutter 1.20 和 iOS 14 上,我們在 iOS 14 發佈的同一天發布了 熱修復)。

無論支援新平台版本需要做多少工作,我們的目標都是支援目標平台的新版本,而不棄用舊版本。

向後相容性和 Flutter 的價值主張

Flutter 是 Google 的可移植 UI 工具包,用於從單一程式碼庫建立針對行動、網頁和桌面的精美的原生編譯應用程式。為此,我們希望支援我們可以支援的最廣泛的設備集合,而不仅仅是特定類型的平台。我們還希望支援最多數量的平台版本。

以 Android 為例:根據 statcounter,截至 2020 年 6 月,市面上的 Android 版本如下所示:

從數字來看,Android KitKat (Android 4.x) 的市場佔有率不到 2%。絕大多數 Android 設備 (54%) 正在運行 Android Pie (Android 9.0) 或 Android 10。支援每個版本的 Android 都需要投資。您知道我們為何要投資於像 KitKat 這樣市場佔有率低於 2% 的平台嗎?

答案很簡單:淨數字。由於超過 10 億台 Android 設備沒有運行 Android Pi 或更高版本,我們希望確保 Flutter 尽可能广泛地得到使用。我們還希望支援多元的全球使用者群。而且,由於 Android 和 iOS 的舊版本經常被開發中國家的使用者使用,我們希望在尽可能長的時間內避免停止支援舊版本。

透過 Flutter 的核心引擎、框架和 Plugin 中的工作,Flutter 為作業系統平台和作業系統版本的各種問題提供了抽象。例如,Material 應用程式可以在舊版本的 iOS 或 Android 上運行,而无需進行任何修改。事實上,開發人員已發佈在與第一個版本同時推出的硬體上運行的商業 Material 應用程式,而使用這些設備附帶的 Widget 集,則無法在這些設備上執行 。Plugin 也有類似的案例。Plugin 會抽象化平台和版本差異,讓您可以專注於重要的部分:您的應用程式。

Flutter 如何支援如此多的目標

Flutter 為多目標支援帶來了兩大優勢。首先,Flutter 對底層作業系統的依賴性很小。Flutter 的引擎直接與圖形 API 互動,Plugin 為其他功能提供了大部分的設備特定實作。

我們的持續整合 (CI) 系統會為 macOS、Linux 和 Windows 建立 Flutter 及其工具,並部署到一系列 Android 和 iOS 設備(實體設備和模擬設備)。部分 CI 是使用 Google 辦公室中的實驗室進行的;其他 CI 測試是在 Firebase 測試實驗室 上進行的,這是 Google 為 Google 和第三方開發人員運營的雲端託管測試解決方案。

以下是一張我們測試實驗室中其中一個機架的照片:

我們在設備實驗室中運行相對低端的行動設備,因為我們會在那裡進行 效能測試。在測試時,我們希望從代表大多數使用者擁有的設備中獲取效能指標。這樣做可以讓我們獲得最糟糕的情況、真實世界的效能。這種理念也適用於核心工程團隊使用的設備。核心工程團隊的大部分成員並沒有在最新的 Google Pixel 或 Apple iPhone 上進行 Flutter 除錯。

由於我們想要測試許多類型的行動設備,因此我們會保留一個設備庫,核心工程團隊的成員可以在其中檢出特定設備以進行測試和除錯。(在新冠肺炎疫情期間,我們充分利用了美國郵政服務!)

由於實驗室只能測試少數幾種設備類型和版本,我們如何確保我們正在提供我們聲稱提供的支援?請繼續閱讀以瞭解。

對平台版本支援做出一般性斷言

首先也是最重要的是,我們會針對每個程式碼提交在各種平台上進行測試。

在我們目前的實驗室中,我們正在以下 Android 平台上進行測試:

  • Android API 24 (Android N)
  • Android API 28 (Android P)
  • Android API 29 (Android 10)

這涵蓋了已部署 Android 版本市場的 57%;稍後,我們會說明如何透過使用 Firebase 測試實驗室來涵蓋剩餘的設備。

我們的實驗室還會針對每個提交在運行以下作業系統的設備上測試 iOS:

  • iOS 9.3.6
  • iOS 12.4
  • iOS 12.4.1
  • iOS 13.1.3
  • iOS 13.2

最後,對於桌面和網頁,我們會針對每個提交在以下設備上進行測試:

  • Chrome 84
  • Firefox 72.0
  • Catalina 上的 Safari
  • Edge 1.2.0.0
  • Windows 10
  • macOS El Capitan
  • Debian 10

很明顯,Android 方面需要進行更多測試。我們依賴於 Firebase 測試實驗室 完成這項工作,測試以下額外的 Android 和 iOS 組態:

  • Android API 19 (Android K)
  • Android API 21 和 22 (Android L)
  • Android API 23 (Android M)
  • Android API 26 和 27 (Android O)
  • Android API 30 (Android 11)
  • iOS 11
  • iOS 13.0
  • iOS 13.1
  • iOS 13.3–13.7
  • iOS 14

總體而言,我們對 Android 和 iOS 平台的測試涵蓋了目前發佈版本中的 95% 以上。

對於其他作業系統版本,我們透過 您的 支援,也就是 Flutter 開發社群,提供向後相容性。這對特定的設備/作業系統組合尤其適用。你們中許多人使用我們無法使用到的設備:可能是因為你們的作業系統版本我們沒有,或是因為這些設備在美國無法取得。在某些情況下,我們可以減輕這種情況。例如,我們可以在一些 Google Pixel 硬體上刷入較舊的 Android 版本,購買較舊的翻新 iOS 設備,或在模擬器中運行 Android 版本。在許多情況下,你們中的一個人提交的報告會觸發我們對特定平台問題的調查。

這對許多 Android 設備的原始設備製造商 (OEM) 尤其適用。OEM 通常會對 Android 的某些方面進行自訂,例如鍵盤支援。過去,由於你們的幫助,我們能夠修復許多僅在某些市場的某些設備上發生的文字輸入問題。

我們感謝您的幫助,因為我們不可能自己找到所有問題,但我們會努力降低注入問題的風險。在進行程式碼更改時,我們會謹慎行事,並注意作業系統的最早版本支援哪些功能。在需要支援不連貫的組態時,我們會提供選項。這就是為什麼您可以使用 OpenGL 或 Metal 建立 iOS 應用程式,Flutter 會在運行時自動選擇其中之一。

由於這些原因,儘管我們會盡力支援作業系統版本上的測試,但我們支援的一些設備是基於「盡力而為」的基礎支援的。我們會嘗試撰寫支援我們聲稱支援的平台的程式碼,我們會依賴團隊對設備多樣性的隨機使用承諾,然後會尋求社群的回饋,以了解我們在實際使用中表現如何。

我們需要您幫助測試屬於「盡力而為」類別的設備。此類別中有數萬(甚至更多!)個 SKU。我們會盡可能地進行測試,有時候我們會從拍賣網站取得較舊的設備,以方便我們測試和修復在無法複製問題時通報的設備問題。如果您發現特定設備的問題,請在 GitHub 上提交問題。

最後,一些平台,例如 iOS 7,已被棄用。也許它們可以正常工作,但我們不會在已被棄用的平台上進行測試,也不提供任何保證。但是,如果我們可以在修補程式生效後測試時充分支援這些平台,我們會根據具體情況考慮針對已被棄用的平台的修補程式。

Flutter 會維護一個 網頁,列出我們會在其中進行測試的設備以及我們會尋求社群幫助的設備。

決定停止支援

儘管我們致力於在尽可能長的時間內支援每個平台版本,但支援舊平台(或平台組態,例如 Apple A7 上的 OpenGL)需要付出代價,並且會遵循我們上游合作夥伴(包括 Skia)的指南。Flutter 的核心工程團隊規模相對較小,我們會不斷做出權衡,以確保我們所做的事情對使用者來說最有利。在某些時候,工程方面、CI 實驗室中測試的頻寬以及隨機測試的頻寬,以及技術限制(例如作業系統供應商停止支援平台版本中的工具)的成本可能會讓繼續支援目標平台變得困難或不可能。

在我們決定停止支援之前,我們會考慮以下一些問題:

  • 這將會減少我們開發人員的多少終端使用者?
  • 這對 Flutter 開發人員有何影響?是否有主要合作夥伴的業務運營會受到重大影響?
  • 我們可能會節省多少工程成本?例如,透過支援多個作業系統版本,使用 Flutter 建立的應用程式數量是否會增加?(例如,在 iOS 版本中支援 OpenGL 和 Metal 就是這樣的案例。)
  • 這個決定是否符合我們廣泛採用和包容性的目標?
  • 停止支援是否是由外部原因引起的,例如平台供應商在其建置工具中停止支援平台版本?如果是,是否有可能減輕這種停止支援的影響?

在評估是否停止支援時,我們會先透過在 GitHub 中開啟一個議題來詢問您的意見,並發布一份相關的 Flutter 設計文件,該文件會提出停止支援的建議,說明我們為何考慮停止支援,以及可能可用的緩解措施。這是一個向社群徵求意見的請求 (RFC);公共設計文件會透過我們的討論清單 ([email protected]) 和我們的 Discord 推廣。

若要開始停止支援,我們會在發佈穩定版本時,作為穩定版本公告的一部分,啟動 RFC 流程。然後,我們會留出時間供公眾發表意見,然後在後續平台版本中減少對平台或功能的支援。RFC 是您提供回饋的機會。根據我們收到的回饋,我們將繼續停止支援,或對提議進行修改,將平台版本的支援時間延長。我們會認真对待這些前瞻性決定的回饋意见,因為我們認識到我們對您和您的產品使用者負有責任。

徵求意見:停止支援 iOS 8

我們正在考慮停止支援 iOS 8。所有 iOS 8 設備都可以升級到 iOS 9。根據 Google 和第三方分析,運行 iOS 8 的設備數量非常少(一些報告顯示「0.0%」),而 Apple 正在逐步停止支援其工具中的 iOS 8。

我們已發布了 有關停止支援 iOS 8 的徵求意見請求,我們請求您審閱並參與討論。我們的目的是使 Flutter 1.22(我們最新的穩定版本)成為最後一個支援 iOS 8 的穩定版本。RFC 包含理由以及如果您受到影響,該如何操作。

徵求意見:停止支援 Android Jelly Bean

我們正在考慮停止支援 Android Jelly Bean,因為我們不再在實驗室中測試運行該版本的 Android 設備,而為實驗室取得可靠的設備也越來越困難。目前運行 Android Jelly Bean 的設備約佔 Android 使用者群體的 0.46%。

我們已發布了 有關停止支援 Android Jelly Bean 的徵求意見請求,我們請求您審閱並參與討論。我們的目的是使 Flutter 1.22(我們最新的穩定版本)成為最後一個支援 Android Jelly Bean 的穩定版本。RFC 包含理由以及如果您受到影響,該如何操作。

您可以如何幫助

最簡單的幫助方式是參與!我們感謝您提交有關我們尚未遇到的各種設備的邊緣案例的問題。在許多情況下,這些問題出現在我們從未擁有的硬體上,即使我們將硬體測試的支出增加一倍或四倍。報告這些問題,並提供可重現的情況和有關軟體版本和硬體型號的詳細資訊,對我們追蹤問題的根源至關重要。

此外,我們希望您在需要減少對特定平台的支援時,參與決策過程。當我們考慮 Flutter 的未來時,您的見解非常寶貴。請在您看到 RFC 時參與我們公開的 RFC。您是您特定市場的專家,也是離您的客戶最近的人。

我們非常感謝您對我們使用者和讓我們成為您產品一部分的信任。我們仍然致力於幫助您將您的願景呈現在尽可能多的螢幕上。


大規模提供作業系統相容性 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

\n

【文章翻譯】Updates on Flutter Testing

【文章內容使用 Gemini 1.5 Pro 自動產生】

更新整合測試,支援行動端的 Firebase Test Lab;同時也支援網頁和桌面端的獨立支援!

Flutter 的目標是提供一個開放的框架,用於在多個平台上快速構建精美的原生應用程式。實現這個目標的很大一部分是能夠輕鬆地測試您的 Flutter 應用程式,以確保它們按您的意願工作,並在您所針對的平台上以您想要的方式呈現。一些測試可以透過使用 內建於 Dart 的單元測試框架 編寫的單元測試來處理。Dart 單元測試對於非 UI 測試非常有效;它在您的開發機器上執行,並且不依賴於 Flutter 應用程式的 GUI。

整合測試(也稱為端到端測試或 GUI 測試)比單元測試更進一步,因為整合測試嘗試透過按下按鈕、選擇項目和在鍵盤上輸入等方式模擬使用者與您的應用程式互動。這種測試是自動的,以避免讓人類做這種重複性的工作,因為坦白說,我們不擅長這種事情。我們最初避免人類互動的解決方案是一種編寫 Flutter 測試的特殊方式。這些測試從主機執行,就像 Dart 單元測試一樣,並驅動在真實或虛擬設備上運行的應用程式,就像一個人一樣。這些測試稱為 Flutter 驅動測試,因為您使用 flutter_driver 套件和 flutter drive 命令列來驅動應用程式的 GUI。

不幸的是,Flutter 驅動測試存在一些問題。一個問題是測試從開發機器執行並與設備上的應用程式通訊,這意味著測試不適合在像 Firebase Test Lab 這樣的設備池上執行。另一個問題是,為您的測試單獨使用一個程序會讓您很難檢查應用程式的狀態。您可以檢查輸出,但是您如何知道,例如,應用程式的內部狀態。最後,flutter_driver API 比它需要的要複雜,特別是在編寫程式碼以找到螢幕上適當的 Widget 來進行測試時。

因此,為了解決這些問題並繼續改進 Flutter 測試體驗,使其涵蓋越來越多目標,我們很高興宣布 integration_test Plugin 的 1.0 版,這是一種更簡單的方式來測試您的 Flutter 應用程式,並且也支援 Firebase Test Lab。

開始使用 integration_test

使用 integration_test Plugin 需要兩個步驟。首先,將 Plugin 添加到您的 pubspec.yaml 檔案中,作為開發相依,並使用 flutter pub get 將 plugin 拉到您的專案中:

1
2
3
4
5
6
7
# pubspec.yaml

dev_dependencies:
flutter_test:
sdk: flutter
integration_test: ^1.0.0

然後,在測試程式碼中使用 integration_test 套件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

group('end-to-end', () {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
app.main();
await tester.pumpAndSettle();

// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);

// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pumpAndSettle();

// Verify the counter increments by 1.
expect(find.text('1'), findsOneWidget);
});
});
}

請注意 integration_test 套件的匯入和對 IntegrationTestWidgetsFlutterBinding.ensureInitialized() 的呼叫,這將確保套件正確初始化。還要注意標準 WidgetTester 測試程式碼。這是在 flutter create 期間由預設的 Counter 應用程式範本生成的相同測試程式碼。

雖然 integration_test 使您的測試能夠以獨立的方式捆绑到您的應用程式中(這是像 Firebase Test Lab 這樣的設備池的要求),但它需要一些我們將在下面介紹的構建技巧。但是,在測試開發過程中,能夠透過命令列互動式執行您的測試非常方便,为此,您需要一個新的入口點:

1
2
3
4
5
6
import 'package:integration_test/integration_test.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationDriver();
}

integrationDriver 的呼叫是 integrationDriver Plugin 的便捷包裝函數,使您能夠使用 flutter drive 命令執行新的測試:

1
2
3
$ flutter drive 
--driver=test_driver/integration_test.dart
--target=test/widget_integration_test.dart

此命令會將您的應用程式部署到模擬器,執行您的測試,並向您顯示結果。

Flutter integration_test in action

此特定測試在 iOS 上執行,但 integration_test Plugin 也適用於 Android,只需更改 --device-id 選項,就像適用於其他平台一樣。此外,您也可以針對網頁和桌面目標執行整合測試,但此功能仍處於預發佈階段。

在 Firebase Test Lab 上執行

當您知道測試可以使用虛擬或實體硬體在本地執行後,就可以將應用程式在 Firebase Test Lab 提供的大量設備上執行。

Firebase Test Lab console

Firebase Test Lab 是一個基於雲端的應用程式測試基礎設施。透過一項操作,您可以在各種設備和設備配置上測試您的 Android 或 iOS 應用程式,並在 Firebase 主控台中查看結果 - 包括日誌、影片和螢幕截圖。integration_test Plugin 的一大進步是能夠在 Firebase Test Lab 上執行針對 Android 和 iOS 的 Flutter 應用程式,讓您能夠在發佈應用程式之前,在數百台設備上同時測試,以查找平台、外觀因素或設備特定的問題。

若要在 Firebase Test Lab 上執行測試,您需要進行一些配置,並使用 Gradle 命令為 Android 和 iOS 構建測試工具,這取決於您選擇的平台。有關這些詳細資訊,請查看 flutter.dev 上最新的整合測試文件

從 Flutter 驅動測試遷移

對於使用現有的 flutter_driver 測試的使用者,遷移到新的 API 並不困難。除了前面提到的適當初始化之外,您還需要遷移到新的 WidgetTester API。

flutter_driver API (left) vs. WidgetTesting API (right)

flutter_driver API(左邊顯示)和 WidgetTester API(右邊顯示)在概念上非常相似,但您可以看到許多細節是不同的。例如,您不是在 flutter_driver 上呼叫 waitFor 方法,而是在 WidgetTester 上呼叫 pumpAndSettle 方法。前者等待特定 Widget 出現,後者等待應用程式 UI 渲染階段穩定。獲得特定 Widget 後,您可以使用兩個 API 類似地對其進行操作,但您使用的是不同的物件。WidgetTest API 更符合您在 Dart 單元測試中看到的內容。如您所見,expect 方法用於確保 Widget 的內容符合您的預期。

有關如何將測試从 flutter_driver 遷移到 integration_test 和 WidgetTester 的詳細資訊,請查看 flutter.dev 上的遷移文件

原生 UI 測試

如果您有一個現有的 Android 或 iOS 應用程式,您使用 Add-to-App 為其添加 Flutter,那麼您可能已經為這些原生應用程式有了現有的整合測試,您想要利用這些測試。在這種情況下,將 Flutter 螢幕的測試添加到這些現有的測試中。對於 Android,如果您使用 Espresso 框架編寫測試,可以使用 espresso Plugin,它為 Espresso 對 Flutter Android 應用程式的測試提供了綁定。我們很快就會有類似的 Plugin 來支援使用 Earl Grey 建立的原生 iOS 測試。

總結

本公告介紹了使用適用於 Flutter 的新 integration_test Plugin 的整合測試的新基礎。不僅 API 更簡單,而且更一致,使用 integration_test 編寫的測試可以使用 Firebase Test Lab 在數百台不同的設備上執行。Flutter 團隊計劃透過將 flutter create 範本來使用 integration_test 作為預設值,更新測試輸出以支援使用 JUnit 格式的現有測試工具,添加在測試期間為黃金測試比較截取螢幕截圖的功能等等,來進一步發展這個新基礎。有關針對 Flutter 應用程式和 Plugin 的整合測試新建議的完整詳細資訊,請查看 flutter.dev 上的測試文件


Flutter 測試更新 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

【文章翻譯】Why nullable types?

【文章內容使用 Gemini 1.5 Pro 自動翻譯產生】

為什麼要使用可空類型?

幾週前,我們宣布了 Dart 空安全測試版,這是一項主要的生產力功能,旨在幫助您避免空錯誤。說到空值,在 /r/dart_lang subreddit 中,一位使用者最近問道

但是為什麼我們仍然擁有/想要空值?為什麼不完全擺脫它呢?我目前也在玩 Rust,它根本沒有空值。所以似乎沒有它也能活下去。

我喜歡這個問題。為什麼完全擺脫空值?本文是我在該討論串中回答內容的擴展版本。

簡短的答案是,是的,完全有可能在沒有空值的情況下生存,像 Rust 這樣的語言就是這樣做的。但是程式設計師確實會使用空值,所以在我們可以將其移除之前,我們需要了解為什麼要使用它。當我們在確實擁有它的語言中使用它時,空值通常什麼?

事實證明,空值通常用於表示值的缺失,這非常有用。有些人沒有中間名。有些郵寄地址沒有公寓號碼。有些怪物在你殺死它們時不會掉落任何寶藏。

在這種情況下,我們想要一種表達方式:「這個變數可以具有 X 類型的值,或者它可能根本沒有值。」那麼問題是如何建模呢?

一種選擇是說一個變數可以包含預期類型的值,或者它可以包含魔術值 null。如果我們在值為 null 時嘗試使用它,就會出現執行時錯誤。這就是 Dart 在空安全之前所做的,SQL 所做的,Java 對非基本類型所做的,以及 C# 對類類型所做的。

但是執行時失敗很糟糕。這意味著我們的使用者會遇到這個錯誤。我們程式設計師寧願在他們遇到之前就發現這些錯誤。事實上,如果我們能夠在我們執行程式之前就發現錯誤,我們會很高興。那麼我們如何以類型系統理解的方式對值的缺失進行建模呢?換句話說,我們如何給「可能缺失」的值和「肯定存在」的值不同的靜態類型?

主要有兩種解決方案:

  1. 使用選項或 maybe 類型
  2. 使用可空類型

解決方案 1:選項類型

這就是 ML 和大多數源自 ML 的函數式語言(包括 Rust、Scala 和 Swift)所做的。當我們知道我們肯定會有一個值時,我們只使用底層類型。如果我們寫 int,則表示「這裡肯定有一個整數」。

為了表示一個可能缺失的值,我們將底層類型包裝在一個 選項類型 中。所以 Option<int> 表示一個值,它可能是一個整數,也可能什麼都不是。它就像一個可以包含零個或一個項目的集合類型。

從類型系統的角度來看,intOption<int> 之間沒有直接關係。將它們視為不同的類型意味著我們不能意外地將可能缺失的 Option<int> 傳遞給預期接收真實 int 的東西。我們也不能意外地嘗試使用 Option<int> 好像它是一個整數一樣,因為它不支援任何這些操作。我們不能對 Option<int> 執行算術運算,就像我們不能對 List<int> 執行算術運算一樣。

要從底層類型的現有值(例如 3)建立選項類型的值,您可以像 Some(3) 一樣構造選項。要在值缺失時建立選項類型,您可以寫類似 None() 的內容。

為了使用儲存在 Option<int> 中的可能缺失的整數,我們必須首先檢查並查看值是否存在。如果存在,我們可以從選項中提取整數並使用它,就像從集合中讀取值一樣。具有選項類型的語言通常也具有良好的 模式匹配 語法,這為我們提供了一種優雅的方式來檢查值是否存在,如果存在則使用它。

解決方案 2:可空類型

另一種選擇 (heh) 是 Kotlin、TypeScript 和現在的 Dart 所做的。可空類型聯集類型 的一種特殊情況。

(題外話:這裡的命名非常令人困惑。選項類型——ML 和它的朋友們上面所做的——是 代數資料類型 的一種特殊情況。代數資料類型的另一個名稱是「區分聯集」。但是,儘管名稱中有「聯集」,但「區分聯集」與「聯集類型」卻大不相同。正如 Phil Karlton 所說,電腦科學中只有兩個難題:快取失效和命名。)

與選項類型方法類似,我們使用底層類型來表示一個肯定存在的值。所以 int 仍然表示我們絕對有一個整數。如果我們想要一個可能缺失的整數,我們可以使用 int? 可空類型。這個小小的問號是 int | Null 之類的聯集類型的語法糖。

就像選項類型一樣,可空類型不支援與底層類型相同的操作。類型系統不允許我們嘗試對可空 int 執行算術運算,因為這是不安全的。同樣,我們不能將可空整數傳遞給需要實際整數的東西。

然而,類型系統比選項類型更靈活一些。類型系統理解聯集類型是其分支的超類型。換句話說,intint? 的子類型。這意味著我們可以將肯定存在的整數傳遞給預期接收可能存在的整數的東西,因為這樣做是安全的。這是一個向上轉換,就像我們可以將 String 傳遞給接收 Object 的函數一樣。Dart 只禁止我們反過來——從可空到不可空——因為那將是一個向下轉換,而這些可能會失敗。

當我們有一個可空類型的值,並且我們想要查看是否存在實際值或 null 時,我們會像在 C 或 Java 中自然地那樣以命令式方式檢查該值:

1
2
3
4
5
foo(int? i) {
if (i != null) {
print(i + 1);
}
}

然後,語言使用 流程分析 來確定程式的哪些部分受到這些檢查的保護。分析確定只有在變數不為 null 時才能到達程式碼,因此在這些區域內,類型系統會將變數的類型收緊為不可空。因此,在這裡,它將 i 視為在 if 語句內具有 int 類型。

語言應該採取哪種解決方案?

因此,當我們 Dart 團隊決定讓語言以更安全的方式處理 null 時,我們應該如何選擇解決方案 1 或 2?我們可以從觀察我們的使用者開始。他們想要如何編寫檢查缺失值的程式碼?在函數式語言中,模式匹配是主要的控制流程結構之一,那裡的使用者對它非常熟悉。使用選項類型和模式匹配在這種風格中是很自然的。

在源自 C 的命令式語言中,像我之前的範例這樣的程式碼是檢查 null 的慣用方法。使用流程分析和可空類型使熟悉的程式碼能夠正確安全地工作。事實上,在 Dart 中,我們發現大多數現有程式碼在新類型系統下已經是靜態空安全的,因為新的流程分析可以正確地分析已經編寫的程式碼。

(這在某種程度上並不令人驚訝。大多數程式碼在處理 null 方面已經是動態正確的。如果沒有,它會一直崩潰。大部分工作只是使類型系統足夠聰明,以便看到該程式碼已經正確,從而使用者的注意力集中在少數不正確的部分。)

因此,如果我們的目標是最大限度地提高熟悉度和使用者舒適度(這語言設計中的重要標準),我們應該遵循我們語言的控制流程結構為我們設定的路徑。

表示缺失和存在

有一種更深層次的方法來處理這個問題,基於選項類型和可空類型的表示方式之間的差異。這種表示方式的差異迫使我們做出一些關鍵的取捨,而這些取捨可能會使我們傾向於某個方向。

在第一種方法中,選項類型的值具有與底層值不同的執行時表示。假設我們在 Dart 中選擇了選項類型,您建立了一個選項類型,然後將其向上轉換為 Object

1
2
3
var optionalInt = Some(3);
Object obj = optionalInt;
print(obj is int); // false

請注意最後一行。Option<int> 值,即使存在,也不像底層類型的值。Some(3)3 是不同的、可區分的

【文章翻譯】Announcing Dart null safety beta

【文章內容使用 Gemini 1.5 Pro 自動產生】

開始將套件遷移到更健全、更安全的狀態

此文章同時發布於 Dart 部落格 上。

今天,我們宣佈 Dart 和 Flutter 的健全空安全功能已進入 Beta 階段。空安全是我們最新的主要生產力功能,旨在 幫助您避免空錯誤,這是一種通常難以發現的錯誤。如果您想快速了解為什麼我們對空安全感到兴奋,請查看這部新影片:

隨著空安全功能升級到 Beta 階段, 是時候開始對數千個在 pub.dev 上提供的套件進行社群遷移了。我們已經遷移了 Dart 核心函式庫、Flutter 框架以及 超過 40 個 Dart 和 Flutter 套件。我們希望看到社群透過遷移他們的套件來擁抱空安全性。

Timeline of Dart sound null safety support, from Technical Preview 1 to Stable in early 2021

隨著 Beta 版本的发布,我們也開始了在發佈穩定版空安全功能之前的最後衝刺。我們希望您能使用此功能,並告訴我們哪些部分可以改進,哪些 UI 訊息可以變得更容易理解,或者哪些文件可以變得更清晰。我們 **非常期待 您的回饋**。

選擇加入空安全功能

在我們討論空安全功能的遷移之前,重要的是要重申(如我们在空安全原则 中所述),您可以控制何时开始采用空安全功能。應用程式和套件只有在它们的最小 Dart SDK 约束 至少为 Dart 2.12 预览版本时才会使用空安全功能:

1
2
environment:
sdk: ">=2.12.0-0 <3.0.0"

要体验这一点,请尝试创建一个小的空安全版 hello 应用程序(例如,使用 dart create),其中包含如下所示的代码。然后,您可以尝试在更改SDK约束之前和之后运行该应用程序,以及运行 dart pub get,并体验程序行为的變化。(请确保使用在 dart –version 中报告 2.12 的 SDK。)

1
2
3
4
5
6
7
8
9
**bin/hello.dart:**
...
void main() {
var hello = 'Hello Dart developers';
if (someCondition) {
hello = null;
}
print(hello);
}
1
2
**在更改 SDK 约束之前:**
$ dart run
1
null
1
2
**在更改 SDK 约束之后(并运行 dart pub get):**
$ dart run
1
2
3
bin/hello.dart:6:13: Error: Null can't be assigned to a variable of type 'String' because 'String' is not nullable.
hello = null;
^

遷移到空安全功能

要將套件(或簡單的應用程式)遷移到空安全功能,請遵循以下五个步骤,这些步骤在 dart.dev 上的 遷移指南 中有完整的說明。

第 1 步:檢查您的相依性是否已準備好

我們強烈建議您按照順序遷移程式碼,先遷移相依性圖的葉子节点。例如,如果 C 相依於 B,而 B 相依於 A,請先將 A 遷移到空安全功能,然后是 B,最后是 C。此顺序适用于 A、B 和 C 是函式庫、套件还是应用程序。

Illustration of dependency order vs. migration order

為什麼順序很重要?雖然您可以在相依性遷移之前取得一些遷移程式碼的進展,但如果您在相依性遷移期間更改其 API,您可能會冒著需要進行第二次遷移的風險。如果您的幾個相依性不是空安全的,請考慮使用在 pub.dev 上列出的每個套件的聯絡資訊來聯繫套件發佈者。

驗證相依性是否已準備好

若要驗證您的應用程式或套件是否已準備好開始遷移,您可以在空安全模式下使用 dart pub outdated。以下範例顯示,如果此應用程式將其相依性升級到如 可解析 欄位中列出的路徑、進程和 pedantic 的預覽版本,則它已準備好進行遷移。

Screenshot of `dart pub outdated` output

如果空安全支援在次要新版本中可用,您將在 可升級 欄位中看到它們。通常,空安全支援將在主要新版本中可用;在这种情况下,您将看到在 outdated 输出的 可解析 下列出的版本。若要升級到這些版本,請編輯您的 pubspec.yaml 檔案以允許這些主要版本。例如,您可能會將 process: ^3.0.13 更改為 process: ^4.0.0-nullsafety。

您還可以在 pub.dev 上使用套件頁面上的新 空安全 標籤(例如 collection 1.15)和新的 進階搜尋 空安全搜尋選項 來查找具有空安全支援的套件。

Screenshot of pub.dev search

第 2 步:使用遷移工具進行遷移

如果您的相依性已準備好,您可以繼續使用遷移工具 dart migrate 來遷移您的應用程式或套件。

遷移工具是互動式的,因此您可以審查工具推斷出的可空性屬性。如果您不同意工具的任何結論,您可以加入可空性提示以更改推斷。加入幾個遷移提示可能會對遷移品質產生重大影響。

Screenshot of the migration tool UI

少數 Dart 套件作者已經使用空安全功能的早期預覽版本測試了遷移,他們的回饋令人鼓舞。遷移指南中提供有關如何使用遷移工具的額外提示。

第 3 步:靜態分析您的遷移程式碼

在您的 IDE 或命令列中使用 pub get 更新您的套件。然後使用您的 IDE 或命令列對您的 Dart 程式碼執行靜態分析:

1
2
$ dart pub get
$ dart analyze

或在您的 Flutter 程式碼上:

1
2
$ flutter pub get
$ flutter analyze

第 4 步:確保測試通過

運行您的測試,并确保它们通过。您可能需要更新預期為空值的測試,以防您更改了套件程式碼,使其不再允許為空。

第 5 步:發佈您的空安全套件

遷移完成且測試通過後,您可以將套件作為預覽版本發佈。以下是最佳實務的簡要摘要:

  • 將您的版本號碼增量到下一個主要版本(例如,從 2.3.x 到 3.0.0)。此最佳實務可確保您的套件使用者不會在他們準備好使用空安全功能本身之前升級到它,並且它讓您能夠自由地重構 API 以最佳地利用空安全功能。
  • 將套件的版本設定為 pub.dev 上的預覽版本。(例如,使用 3.0.0-nullsafety.0,而不是 3.0.0。)

有關遷移和版本控制的完整詳細資訊,請參閱 遷移指南

健全空安全的優點

我們之前關於空安全功能技術預覽的部落格文章 在 Dart 中在 Flutter 中 已經透過許多範例討論了空安全的優點。現在,空安全功能即將完成,我們看到了這種好处的几个实际例子。

更安全的程式碼

就在最近,我們 發現了 Flutter 主頻道中的一个錯誤,其中各種 flutter 工具命令在某些機器設定上會因空錯誤而崩潰:方法 ‘>=’ 被調用於 null 上。潜在问题是最近的一个添加了对检测 Android Studio 4.1 的支持的拉取请求。该 PR 添加了类似于以下内容的代码:

1
2
3
4
5
6
final int major = version?.major;
final int minor = version?.minor;
if (globals.platform.isMacOS) {
/// Android Studio 的插件路径在 4.1 版本之后发生了变化。
if (major >= 4 && minor >= 1) {
...

你能找到错误吗?由于 version 可能会为 null,major 和 minor 也可能会为 null。这个错误在孤立情况下似乎很容易发现,但在实践中,即使是像 Flutter 存储库中使用的严格代码审查流程,也会经常出现类似的代码。使用空安全功能,静态分析 会立即发现这个问题

Screenshot of analysis output in an IDE

这是一个非常简单的错误。在 Google 内部早期使用空安全功能的代码中,我们已经看到更多复杂的错误被发现,然后通过空安全功能解决。以下是几个例子:

  • 一个内部团队发现,他们经常检查代码中的空值,而空安全功能知道这些空值永远不会为 null。这个问题最常出现在使用 protobuf 的代码中,其中可选字段在未设置时返回默认值,而不会返回 null。这导致代码错误地检查默认条件,因为混淆了默认值和 null 值。
  • Google Pay 团队发现他们的 Flutter 代码中的错误,这些错误在尝试在 Widget 上下文之外访问 Flutter State 对象时会导致失败。在空安全功能之前,它们会返回 null 并隐藏错误;使用空安全功能,健全的分析确定这些属性永远不会为空,并抛出一个分析错误。
  • Flutter 团队发现一个错误,如果将 null 传递给 Window.render() 中的 scene 参数,Flutter 引擎可能会崩溃。在空安全功能迁移期间,他们添加了一个提示,以 将 Scene 标记为不可为空,并且能够轻松地防止潜在的应用程序崩溃,如果传递了 null,这些崩溃可能会被触发。

在編譯過程中利用健全的空安全功能

Dart 空安全功能的健全性還有一個令人歡迎的含義:這意味著 Dart 編譯器可以利用可空性資訊。這可能會使您的程式更小、更快。我們還沒有很多實際應用程式完全遷移到健全的空安全功能(因為我們現在才開始進行這些應用程式相依的套件的生態系統遷移),但我們從核心框架中看到了非常令人鼓舞的結果。

我們最近對 hello_world 範例進行了測試重新編譯,以測量空安全功能對應用程式大小的影響。這是一個最簡單的範例,它只會顯示「hello world」。在 比較 編譯後程式碼的總體大小時,未壓縮的(安裝在設備上的)程式碼大小縮小了 3.5%,而沒有做任何其他事情,只是使用健全的空安全功能重新編譯。這有可能實現,儘管這個應用程式只有 10 行程式碼,因為所有包含的函式庫的程式碼大小都縮小了;例如,Flutter 框架本身(套件:flutter)縮小了 3.9%。

至於程式碼速度,強制執行健全的類型系統可能會增加開銷。但是,減少空檢查也可能會使程式碼執行得更快。我們對基准測試的初步分析表明,效能與以前的版本相當,並且新的額外類型資訊為我們在未來進行新的效能改進提供了潛力。我們計劃在未來的部落格文章中更多地介紹我們的效能工作。

在某些情况下,我们已经看到空安全功能带来了性能提升,通常是在迁移到空安全功能时发现了代码逻辑中的缺陷。例如,我们在 Flutter 网页文字布局缓存中发现了一个问题。这个缓存使用了一个可为空的键,然后使用了一些逻辑来在为空时使用 TextAlign.start。这种逻辑导致缓存出现缺陷,即使元素仍然具有默认值,它们看起来也会像改变了一样。因此,经常出现缓存未命中。添加一个不可为空的 textAlign getter 有助于修复缓存缺陷,从而在文字被缓存的情况下,使文字渲染性能 提高了 14 倍

立即開始吧!

包含空安全功能的 Dart 和 Flutter 的 Beta 版本今天已準備就緒。如果您使用 Flutter 開發,您可以使用 flutter channel beta 然後 flutter upgrade 切換到 Beta 版本。否則,您可以從 Dart SDK 檔案 中獲取獨立的 Dart SDK。

如果您開發套件,我們鼓勵您閱讀我們的 遷移指南 並規劃您的遷移。請 告知我們 您遇到的任何問題或建議。

如果您是應用程式開發人員,您可能更希望延遲遷移,直到該功能出现在我們的穩定通道中。我們計劃快速解决 Beta 版本的回饋,修复任何剩余的问题。很难确定空安全功能将在稳定版中发布的确切时间,但我们预计将在明年年初。

感謝您在我們努力使 Dart 變得更加健壯,以及使 Flutter 變得更加强大的過程中提供支持和回饋!


宣佈 Dart 空安全功能 Beta 版本 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

【文章翻譯】Announcing Dart null safety beta

【文章內容使用 Gemini 1.5 Pro 自動翻譯產生】

宣佈 Dart 空安全 Beta 版

此文章交叉發佈於此處和 Flutter 部落格

今天我們宣佈 Dart 和 Flutter 的 Beta 版已提供完善的空安全功能。空安全是我們最新的主要生產力功能,旨在幫助您避免空值錯誤,這是一類通常難以發現的錯誤。如果您正在尋找我們為何對空安全感到興奮的快速摘要,請查看這部新影片:

隨著空安全進入 Beta 版,是時候開始社群遷移 pub.dev 上可用的數千個套件了。我們已經遷移了 Dart 核心程式庫、Flutter 架構,以及超過 40 個 Dart 和 Flutter 套件。有了這些,我們希望看到社群透過遷移他們的套件來擁抱空安全。

Timeline of Dart sound null safety support, from Technical Preview 1 to Stable in early 2021

隨著 Beta 版的推出,我們也開始在發佈空安全功能的穩定版本之前的最後階段。我們希望您能使用此功能,並讓我們知道它的哪些部分可以改進,UI 訊息是否可以更容易理解,或者文件是否可以更清晰。我們**非常期待收到您的回饋**。

選擇加入空安全

在我們討論空安全遷移之前,需要重複的是(如我們的空安全原則中所述),您可以控制何時開始採用空安全。只有當應用程式和套件的最低 Dart SDK 限制 至少是 Dart 2.12 預發佈版時,它們才會以空安全模式執行:

1
2
environment:
sdk: ">=2.12.0-0 <3.0.0"

要體驗這一點,請嘗試建立一個小的空安全 hello 應用程式(例如,使用 dart create),其中包含如下所示的程式碼。然後,您可以嘗試在更改 SDK 限制並執行 dart pub get 之前和之後執行應用程式,並體驗程式行為的變化。(確保使用在 dart --version 中報告 2.12 的 SDK。)

1
2
3
4
5
6
7
8
9
// bin/hello.dart:
...
void main() {
var hello = 'Hello Dart developers';
if (someCondition) {
hello = null;
}
print(hello);
}
1
2
# 更改 SDK 限制之前:
$ dart run
1
null
1
2
# 更改 SDK 限制之後(並執行 dart pub get):
$ dart run
1
2
3
4
bin/hello.dart:6:13: Error: Null can't be assigned to a variable of type 'String' because 'String' is not nullable.

hello = null;
^

遷移至空安全

要將套件(或簡單的應用程式)遷移至空安全,請按照以下五個步驟操作,這些步驟在 dart.dev 的遷移指南中已完整記錄。

步驟 1:檢查您的依賴項是否已準備就緒

我們強烈建議您按順序遷移程式碼,先遷移依賴圖的葉子。例如,如果 C 依賴於 B,而 B 依賴於 A,則先將 A 遷移至空安全,然後是 B,然後是 C。此順序適用於 A、B 和 C 是程式庫、套件還是應用程式。

Illustration of dependency order vs. migration order

為什麼順序很重要?儘管您可以在依賴項遷移之前在遷移程式碼方面取得一些進展,但如果您的依賴項在其遷移期間更改其 API,則您可能會冒險進行第二次遷移。如果您的某些依賴項不是空安全的,請考慮使用 pub.dev 上列出的每個套件的聯絡方式與套件發佈者聯繫。

驗證依賴項是否已準備就緒

要驗證您的應用程式或套件是否已準備好開始遷移,您可以使用空安全模式下的 dart pub outdated。以下範例顯示,如果此應用程式將其依賴項升級到 可解析 欄中列出的 pathprocesspedantic 的預發佈版本,則它已準備好遷移。

Screenshot of `dart pub outdated` output

如果在新的小版本中提供空安全支援,您將在 可升級 欄中看到它們。通常,在新的大版本中會提供空安全支援;在這種情況下,您將在過時輸出的 可解析 下看到列出的版本。要升級到這些版本,請編輯您的 pubspec.yaml 檔案以允許這些主要版本。例如,您可以將 process: ^3.0.13 更改為 process: ^4.0.0-nullsafety

您還可以使用套件頁面上的新 空安全 標籤(例如 collection 1.15)和新的進階搜尋 空安全搜尋選項 在 pub.dev 上找到具有空安全支援的套件。

Screenshot of pub.dev search

步驟 2:使用遷移工具進行遷移

如果您的依賴項已準備就緒,則可以使用遷移工具 dart migrate 繼續遷移您的應用程式或套件。

遷移工具是互動式的,因此您可以查看工具推斷出的可空性屬性。如果您不同意工具的任何結論,您可以加入可空性提示以更改推斷。加入一些遷移提示可以對遷移品質產生巨大影響。

Screenshot of the migration tool UI

我們已經讓少數 Dart 套件作者使用空安全的早期預覽版本測試遷移,他們的回饋令人鼓舞。遷移指南中包含有關如何使用遷移工具的更多提示。

步驟 3:靜態分析您遷移的程式碼

在您的 IDE 或命令列中使用 pub get 更新您的套件。然後使用您的 IDE 或命令列對您的 Dart 程式碼執行靜態分析:

1
2
$ dart pub get
$ dart analyze

或者在您的 Flutter 程式碼上:

1
2
$ flutter pub get
$ flutter analyze

步驟 4:確保測試通過

執行您的測試並確保它們通過。您可能需要更新預期空值的測試,以防您更改了套件程式碼以不再允許空值。

步驟 5:發佈您的空安全套件

當遷移完成且測試通過後,您可以將您的套件作為預發佈版本發佈。以下是最佳實務的簡要摘要:

  • 將您的版本號遞增到下一個主要版本(例如,從 2.3.x 到 3.0.0)。此最佳實務可確保您的套件的使用者在準備好自己使用空安全之前不會升級到它,並且讓您可以自由地重構 API 以最佳地利用空安全。
  • 在 pub.dev 上將您的套件作為預發佈版本進行版本控制和發佈。(例如,使用 3.0.0-nullsafety.0,而不是 3.0.0。)

有關遷移和版本控制的完整詳細資訊,請參閱遷移指南

完善空安全的好處

我們之前關於 Dart 和 Flutter 中空安全的技術預覽的部落格文章透過許多範例討論了空安全的好處。現在,隨著空安全即將完成,我們看到了此好處的一些真實範例。

更安全的程式碼

就在最近,我們在 Flutter master channel 中發現了一個錯誤,其中各種 flutter tool 命令會在某些機器配置上因空值錯誤而崩潰:在空值上呼叫了方法 '>='。根本原因是最近的一個拉取請求,用於加入對偵測 Android Studio 4.1 的支援。該 PR 加入了如下程式碼:

1
2
3
4
5
6
final int major = version?.major;
final int minor = version?.minor;
if (globals.platform.isMacOS) {
/// Android Studio 4.1 版之後的 Plugin 路徑已更改。
if (major >= 4 && minor >= 1) {
...

您能發現錯誤嗎?因為 version 可能為空,majorminor 也可能為空。這個錯誤在這裡單獨看來似乎很容易發現,但在實務中,這樣的程式碼總是會溜走,即使是像 Flutter 儲存庫中使用的嚴格程式碼審查流程也是如此。使用空安全,靜態分析立即捕獲此問題

Screenshot of analysis output in an IDE

那是一個相當簡單的錯誤。在我們在 Google 內部程式碼中早期使用空安全的過程中,我們已經看到更多複雜的錯誤被捕獲,然後透過空安全解決。以下是一些範例:

  • 一個內部團隊發現,他們經常在空安全知道永遠不會為空的程式碼中檢查空值。此問題最常見於使用 protobuf 的程式碼中,其中可選欄位在未設定時返回預設值,而不是空值。這會導致程式碼透過混淆預設值和空值來錯誤地檢查預設條件。
  • Google Pay 團隊在他們的 Flutter 程式碼中發現了錯誤,在嘗試在 Widget 上下文之外存取 Flutter State 物件時,它們會失敗。在空安全之前,這些會返回空值並掩蓋錯誤;使用空安全,完善的分析確定這些屬性永遠不會為空,並拋出分析錯誤。
  • Flutter 團隊發現了一個錯誤,如果將空值傳遞給 Window.render() 中的 scene 參數,Flutter 引擎可能會崩潰。在空安全遷移期間,他們加入了一個提示以將 Scene 標記為不可為空,然後能夠輕鬆地防止如果傳遞空值會觸發的潛在應用程式崩潰。

在編譯期間利用完善的空安全

Dart 空安全的完善性還有另一個令人歡迎的含義:這意味著 Dart 編譯器可以利用可空性資訊。這可能會使您的程式更小、更快。我們還沒有很多真實世界的應用程式完全遷移到完善的空安全(因為我們現在才剛剛開始這些應用程式所依賴的套件的生態系統遷移以實現完善性),但我們從核心架構中看到了非常令人鼓舞的結果。

我們最近對 hello_world 範例進行了測試重新編譯,以測量空安全對應用程式大小的影響。這是一個最小化的範例,只顯示「hello world」。在比較編譯程式碼的總大小時,未壓縮的(安裝在設備上的)程式碼大小縮小了 3.5%,除了使用完善的空安全重新編譯之外沒有做任何事情。儘管這個應用程式只有 10 行程式碼,但這也是可能的,因為所有包含的程式庫的程式碼大小都縮小了;例如 Flutter 架構本身(package:flutter)縮小了 3.9%。

至於程式碼速度,必須強制執行完善的型別系統可能會增加開銷。但是,進行更少的空值檢查也可能會使程式碼更快。我們對基準測試的初步分析顯示,效能與以前的版本相當,並且新的額外型別資訊為我們未來進行新型別的效能改進創造了潛力。我們計畫在未來的部落格文章中詳細介紹我們的效能工作。

在某些情況下,我們已經看到空安全會帶來效能提升,通常是在遷移至空安全時發現了程式碼邏輯中的缺陷。例如,我們在 Flutter 網頁文字佈局快取中發現了一個問題。此快取使用的鍵是可為空的,然後是一些在為空時使用 TextAlign.start 的邏輯。此邏輯導致快取中的缺陷,即使元素仍然具有預設值,它們看起來也像已更改。因此,經常發生快取未命中。加入一個不可為空的 textAlign getter 有助於修復快取缺陷,從而在文字被快取的情況下使文字渲染效能提高 14 倍

立即開始!

包含空安全的 Dart 和 Flutter 的 Beta 版本今天已經準備好了。如果您使用 Flutter 進行開發,您可以使用 flutter channel beta 切換到 Beta,然後使用 flutter upgrade。否則,您可以從 Dart SDK 存檔 中獲取獨立的 Dart SDK。

如果您開發套件,我們鼓勵您閱讀我們的遷移指南並規劃您的遷移。如果您有任何問題或建議,請告知我們

如果您是應用程式開發人員,您可能希望延遲遷移,直到該功能進入我們的穩定頻道。我們計畫快速處理 Beta 版的回饋,修復任何剩餘的問題。很難說明空安全何時會在穩定版本中發佈的具體時間表,但我們認為是明年年初。

感謝您的支援和回饋,我們正在努力使 Dart 成為更強大的語言,使 Flutter 成為更強大的架構!


宣佈 Dart 空安全 Beta 版 最初發佈在 Dart 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

【文章翻譯】Deprecation Lifetime in Flutter

【文章內容使用 Gemini 1.5 Pro 自動產生】

Flutter 中的棄用生命週期

新的一年即將到來,Flutter 團隊正在為 2021 年的全新開始做好準備。

為此,我們將從 Flutter 架構中移除一些已棄用的 API。

在 Flutter 架構中移除棄用並非新鮮事。我過去也曾經 自己處理過快速搜尋 結果顯示,過去移除棄用沒有定義的時間軸或排程,因為我們的 棄用策略 並沒有規定何時移除它們。由於我們希望為使用者提供一致且可靠的體驗,因此我們決定為棄用建立標準生命週期。

本文將討論我們目前針對重大變更和棄用的策略,以及這些策略如何付諸實行,以及如何發展以為使用者提供更好的體驗。

Flutter 中的重大變更

Photo by CHUTTERSNAP on Unsplash

在 Flutter 中棄用程式碼的流程始於我們的 重大變更策略。棄用被視為重大變更,這意味著它們必須遵循 公開設計文件 指南,該指南要求一段時間用於討論。(此提案的設計文件為 實作棄用生命週期。) 除了提供設計文件外,我們還會在 Flutter 貢獻者 Discord 和受歡迎的社群,例如 flutter-announceflutter-dev 上發佈訊息,所有這些都在進行任何變更之前。這是我作為工程師最喜歡的流程之一:我能夠聽到您的意見。我們很棒的社群在這個流程中非常活躍,這也讓我們變得更好。在多次場合中,我看到一個功能的設計在透過這些管道發佈後有了很大的改進,這全都是因為社群的回饋。

重大變更的作者會遷移受重大變更影響的客戶程式碼。在我們重大變更策略的最後一次更新中,我們 邀請 您將測試提交到我們的 客戶測試儲存庫。這些測試是我們標誌著變更會造成中斷的訊號,我們承諾會在進行變更之前與測試作者合作遷移他們的程式碼。

我們還為我們的重大變更提供 遷移指南。這些指南旨在幫助使用者在更新到最新版本的 Flutter 後遷移他們的程式碼。如果您看過我們的 發行公告 之一,我們也會在特定版本中指出中斷,並引導使用者到有用的資源,幫助他們更新應用程式。

過去的調查 中,我們詢問使用者他們對重大變更的偏好。我們了解到使用者認為重大變更使程式碼更乾淨,對他們很有價值,這就是為什麼我們通常更喜歡重大變更而不是棄用的原因。根據研究:

總體而言,78% 的人表示他們更喜歡導致最乾淨的 API 的重大變更;這也使 API 更易於學習和使用。

我們的重大變更策略基於此研究。與所有規則一樣,總是會有例外。我最近 棄用了一些非常受歡迎的方法,改用新的 Widget。如果我們知道特定變更會造成重大中斷,影響許多使用者,我們就會棄用。棄用允許遷移緩衝期,同時仍然提供重大變更策略所產生所有工具。

日漸老舊的棄用

Photo by Dilyara Garifullina on Unsplash

遷移緩衝期從棄用在 穩定頻道 上發佈時開始。緩衝期結束的時間軸為 1 個日曆年或 4 個穩定版本,取較長者。為了制定出我們認為舒適的時間軸,我們查看了我們的成長率和採用率,並評估了一些已公開發佈的 Flutter 應用程式和套件。

我們希望使用者能夠預期這個排程,而不是擔心下一個版本是否會因為棄用消失而帶來意外的升級障礙。我們還希望確保我們提前通知這些變更,並且我們承諾在開始移除棄用之前,會宣布棄用即將結束生命週期。本著這種精神,我想要與您分享 第一批即將結束生命週期的棄用,現在 Flutter 1.22 已推出。

由於這是我們第一次實施這項策略,因此有很多棄用,其中有些甚至早於 Flutter 1.0!其中一些棄用也早於我們的重大變更策略。我們將會為所有這些棄用努力建立遷移工具和指南。預計在我們下一個穩定版本推出之前,這些棄用將從 Flutter 架構中移除,並會在移除時透過我們通常的管道發布公告。

在我們開始實施這些變更的同時,我們希望聽到您的意見!我們始終歡迎您將測試提交到我們的客戶測試儲存庫,我們會在進行中斷之前與您合作遷移您的程式碼。若要進一步了解測試儲存庫以及如何提交測試,請查看 Flutter 測試 README。在進行這些變更時,我們將透過 Flutter 貢獻者 Discordflutter-announceflutter-dev 頻道通知您並徵求您的回饋。因此,如果您尚未加入其中一個社群,請務必加入!我們期待收到您的訊息,並與您合作。


Flutter 中的棄用生命週期 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

http://creativecommons.org/licenses/by/4.0/