0%

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

今年邁向跨平台開發的重要里程碑

今天,我們很高興宣布兩個新的 SDK 版本——Dart 3.2 和 Flutter 3.16——以及 Flutter 休閒遊戲工具包的重大更新。

Flutter 3.16

Flutter 和 Dart 的價值主張

在今年年初肯亞內羅畢舉行的 Flutter Forward 上,我們介紹了 Flutter 和 Dart 的新路線圖。兩份路線圖都側重於四個高級主題:突破性圖形體驗、與 Web 和行動平台的無縫整合、早期採用新興架構,以及持續關注開發者體驗。

Flutter Forward 的四個高級主題

在 5 月份的 Google I/O 上,我們 宣布 我們在實現這一願景方面取得了重大進展,包括(但不限於)改進 iOS 上的渲染效能(使用 Impeller),支援 Web 上的元素嵌入,以及完成 Dart 3 中 100% 空安全之旅。

隨著我們接近年終,以下是自那以來的進展更新。

使用 Flutter 和 Dart 提供使用者價值

隨著每個新功能、改進或錯誤修復,整體目標保持不變:提供強大的語言和框架配對,讓您能夠建立美麗、豐富且快速的應用程式。您可以從單一的共享程式碼庫進行開發,為行動、Web 和桌面上面的應用程式使用者提供服務,而無需碎片化您的產品路線圖。我們也致力於開源,沒有授權成本,並致力於開發由開放性和協作驅動的豐富生態系統。

這個目標繼續引起 Google 內部和全球 Flutter 廣泛生態系統中開發者的共鳴。今年發佈了許多使用 Flutter 建立的 Google 應用程式:

  • 正如我們在 Flutter Forward 上宣布的那樣,Google Classroom 團隊在其行動應用程式中使用 Flutter,這些應用程式現在已在 iOS 上推出,並且在 Android 上處於 Beta 測試階段。他們正在看到由新的 Impeller 渲染引擎驅動的 顯著效能提升
  • YouTube Create 是一款由 Google 發佈的全新 Flutter 應用程式,它在 8 個國家/地區中提供 Beta 測試。此應用程式利用 Flutter Widget 函式庫的靈活性,提供完整的影片編輯體驗,包括濾鏡、效果以及音樂和配音編輯。
  • 使用Flutter驅動的 Google Earth 10 版本,使用者可以輕鬆地建立、協作和組織地圖和地理空間資料,不僅可以在 Web 上使用,還可以在 Android 和 iOS 行動裝置和平板電腦上使用!查看 Google Earth 發文 以獲取有關新設計的更多詳細資訊,並繼續關注我們將在未來的部落格文章中詳細介紹 Earth 團隊如何使用 Flutter 構建他們的應用程式。
Classroom(左)、YouTube Create(中)和 Google Earth(右)的螢幕截圖

Flutter 的採用也持續在 Google 之外、全球範圍內增長:

  • 今年發佈的應用程式包括瑞典的 Skandia 和波蘭的 Credit Agricole 等銀行應用程式,美國的 MarketWatch 虛擬股票交易所投資教育應用程式,荷蘭的 CZ 健康保險應用程式,以及加拿大的 Kijiji 分類廣告市場應用程式。一個共同的主題是,這些團隊選擇 Flutter 來實現 Android 和 iOS 之間一致的功能集,同時仍然提供豐富且引人入勝的 UI 體驗。
  • 繼續朝東前進,騰訊發佈了 Cloud Chat,這是一款豐富的全新聊天 SDK 和 UI 函式庫,目前在中國的聊天服務市場排名第一,並且正在擴展到國際市場。
  • 在巴西,SOMA 時尚集團啟動了其 Farm 品牌的重大數位更新,並在此過程中意識到 Flutter 的靈活性讓他們能夠將其擴展到該集團的其他品牌。在美國,Caribou Coffee 將 Flutter 與 Firebase 結合,以加速其生產力,而 Lucid Motors 報告稱,Flutter 不僅讓他們可以使用一個小型團隊交付兩個行動應用程式,而且 Flutter 的 Web 支援還讓他們能夠在內部分享原型。

我們很高興見證這些應用程式的演變。繼續閱讀,了解我們在核心框架中取得的持續進展。

突破性圖形效能

我們堅信,多平台開發不應意味著開發者或終端使用者體驗的下降。因此,我們竭盡全力實現這一願景。在 Flutter Forward 上,我們推出了 Impeller 引擎,這是對圖形渲染架構的全面重新設計和重新實作,旨在提高速度和效能。在 Flutter 3.10 中,我們宣布 Impeller 在 iOS 上預設啟用。今天,在 Flutter 3.16 中,我們很高興地分享,Impeller 在 Android 上已準備好在 stable channel 上接受預覽回饋。效能在一整年都得到了顯著提升;與一年前相比,在 Flutter Gallery 基準測試中測量的光柵化效能大約是使用 Impeller 在 Vulkan 上的效能的兩倍。有關如何啟用 Impeller 的詳細信息,請參閱 Flutter 3.16 發文

Flutter Gallery 基準測試顯示,過去一年中使用 Impeller 在 Vulkan 上的效能提高了 2 倍以上

休閒遊戲工具包

特別受益於快速且強大的圖形渲染的應用程式類別是遊戲。在 2022 年,我們發佈了最初的 Flutter 休閒遊戲工具包。今天,我們宣布重大更新,其中包括從紙牌遊戲到跑酷遊戲的全新模板選項,以及針對排行榜、變現和遙測等主題的一組遊戲服務開發者資源。有關更多詳細信息,請查看 休閒遊戲工具包部落格發文

一個動畫 GIF,顯示了在行動裝置、Web 和桌面設備上運行的多款基於 Flutter 的遊戲,例如 Trivia Crack、Debertz 和 Landover。

無縫整合

雖然 Flutter 應用程式通常在不同平台間具有非常高的共享程式碼比例(許多開發者報告共享比例為 95-99%),但我們仍然認為支援平台特定功能非常重要。在 iOS 上,應用程式擴展 提供了一種方式,讓使用者可以從手機上的其他應用程式中與應用程式的縮小版本進行互動。在我們上一個穩定版本中,我們發佈了 新資源 來幫助您為 Flutter 應用程式構建主畫面和鎖定螢幕 Widget。在 Flutter 3.16 中,我們加入了對使用 Flutter 渲染 某些 iOS 應用程式擴展的 UI 的支援,例如 共享擴展。Android 14 推出了新的 預測式返回手勢,它讓使用者可以在使用返回手勢(向左滑動)時瞥見應用程式背後的系統 UI。Flutter 3.16 支援此手勢。

持續關注開發者體驗

現代裝置具有各種外形尺寸(例如手機、摺疊式手機和平板電腦),這些裝置具有各種螢幕尺寸,並支援外部輸入裝置,例如鍵盤和滑鼠。為了確保您的使用者擁有良好的使用者體驗,考慮這些因素非常重要。我們最近與 Android 團隊合作,加入了對其 大型螢幕指南 的支援。我們已經發佈了 在加入大型螢幕支援方面的經驗,更新了我們的參考應用程式 Wonderous 以支援多種螢幕尺寸,並在 Flutter 框架中進行了一些修復,以更好地支援鍵盤、滑鼠和觸控板輸入。新的 API 讓您可以確定顯示器的尺寸和像素比率,並支援多視窗渲染。

在一年中,我們一直致力於完成對 Material 3 的支援,這是 Material Design 設計系統的最新版本。Flutter 的 Material Widget 現在完全支援 Material 3,並且在 Flutter 3.16 中,Material 3 現在是預設樣式。我們發現 Material 為應用程式增添了煥然一新的外觀;新的配色方案既美觀又符合無障礙的對比度要求。若要獲得實際體驗,請查看 Material 3 圖庫

Material 3 演示的螢幕截圖

開發者工具的兩個重大改進包括 VS Code 的新側邊欄和對 DevTools 中添加擴展的支援。側邊欄可以輕鬆發現核心 Flutter 工具操作,例如建立新專案、調用熱重裝、打開 DevTools 或選擇目標設備。DevTools 擴展提供了一個框架,讓您可以建立新的自定義工具,這些工具可以插入 DevTools,並提供額外的框架特定功能。ProviderPatrolServerPod 是開發 DevTools 擴展的幾個框架範例。

Serverpod 框架的 DevTools 擴展的螢幕截圖

我們重新啟動了 Flutter Favorite 程式,並將 flameflutter_animateflutter_rust_bridgeriverpodvideo_playermacos_uifpdart 指定為新的 Flutter Favorite。在 8 月份,我們為套件生態系統舉辦了首次虛擬峰會,超過 50 位非 Google 員工和 Google 員工貢獻者參加了 pub.dev 的峰會。

我們還見證了幾個強大的新套件和 Plugin 的到來。我最喜歡的幾個是 camera Plugin 中的 CameraX 支援,高效能的 2D 可捲軸 TableView 以及 flutterflow_ui 套件,它允許設計師和開發者使用拖放功能建立 UI 組件,然後將它們導出到 Flutter Widget 程式碼(演示影片)。此外,google_maps 套件現在支援 雲地圖樣式,因此開發者可以從 Google Cloud Console 中更新他們的樣式,而 home_widget 套件現在支援建立 互動式 iOS 主畫面 Widget

新興架構

Dart 互操作性

Dart 基於核心承諾,讓跨平台 Dart 程式碼可以在 廣泛的平台 上運行。但是,這通常需要補充以與現有程式碼進行互操作的能力,無論這些程式碼來自舊的專案還是其他函式庫或系統 API 中提供的 API。為了實現這一點,Dart 提供了 與原生 C API 的互操作性,並且正在努力擴展它以支援與 Java + KotlinObjective C + Swift 的互操作性。有關更多詳細信息,請查看今天的 Dart 3.2 部落格發文

WebAssembly 之路

Wasm(也稱為 WebAssembly)是一種 Web 瀏覽器的令人興奮的新指令格式,它提供了一種可移植的、平台中立的、二進位程式碼格式,可以在現代瀏覽器中執行。像 Dart 這樣的高級受控語言使用垃圾收集,而垃圾收集正在被加入到 WebAssembly 標準中。截至 Chrome 119,WebAssembly 的垃圾收集支援(Wasm-GC)預設啟用。Wasm-GC 支援也將在 Firefox 120(他們的下一個穩定版本)中推出。

Dart 編譯器對 Wasm 的支援幾乎已完成,而 Flutter Web 現在使用了一個新的渲染引擎,它可以讓所有渲染都在 WebAssembly 程式碼中進行。雖然還有更多工作要做,支援仍然被認為是實驗性的,但我們已經能夠運行一些應用程式,例如 Material 3 演示Dart 3.2 部落格發文 中有更多詳細信息。

生動社群的力量

今天的部落格發文僅僅是過去幾個季度中數千個改進的示例。更重要的是,越來越多的改進來自Google以外的開發者貢獻——最近發佈的 2023 年 GitHub Octoverse 報告 關於開源活動,將 Flutter 專案列為所有 GitHub 開源專案中的前 3 名。

感謝您的非凡支援,我們度過了美好的一年。對於 Dart 和 Flutter 社群的多樣性、熱情和友善精神,我們表示感謝,他們在產品成功中發揮了至關重要的作用。我們迫不及待地想看看您接下來會建立什麼。在下次更新之前,感謝您的支援 🙏,並祝您編碼愉快!


今年邁向跨平台開發的重要里程碑 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

在 Flutter 中進行免費且開源的跨平台 2D 遊戲開發

今天,我很興奮地向您介紹 Flutter 休閒遊戲工具包 的重大更新,它是一組新的範本和新的開發者資源,旨在讓遊戲開發者在 Flutter 上提高生產力。

Flutter 是 Google 的開源 UI 架構,用於建立美觀的跨平台應用程式。如今,從早期階段的創業公司到 ByteDance、騰訊、BMW 和 Google 等大型公司,Flutter 被用於超過一百萬個應用程式,從單一程式碼庫提供美觀、高效能且可移植的跨平台體驗。

在過去的幾年中,我們目睹了圍繞休閒遊戲開發的社群不斷增長。使用 Flutter 發佈了數以萬計的遊戲,從簡單卻有趣的益智遊戲到更複雜的街機遊戲。我們最喜歡的遊戲包括 Etermax 的 Trivia Crack、Lotum 的 4 Pics 1 Word(猜詞遊戲)、Dong Digital 的 Brick Mania(街機遊戲)、Onrizon 的 StopotS(分類遊戲)、我們在 I/O 為 Flutter 建立的 復古彈珠台遊戲,以及在社交和選單螢幕中使用 Flutter 的 PUBG 行動版。

一個動畫的 GIF,展示了多款基於 Flutter 的遊戲,例如 Trivia Crack、Debertz 和 Landover 在行動裝置、網頁和桌面設備上運行。

為什麼選擇 Flutter 來開發休閒遊戲

Flutter 是遊戲開發者的絕佳選擇。首先,它是免費且開源的,讓您能夠對遊戲的渲染和輸入處理邏輯進行精細的控制。這讓您能夠在核心問題上進行除錯,並根據需要自訂引擎。Flutter 的開放性也體現在我們的生態系統中。所有 Flutter Plugin 和套件 也可供免費整合。

其次,在 Flutter 中進行開發效率很高。Flutter 引入了革命性的功能,稱為 熱重裝,允許開發者在進行程式碼更改後立即看到 UI 更新,從而使開發過程更加反覆和高效。此外,Flutter 支援跨平台遊戲開發,因此您可以從單一共享程式碼庫為 iOS 和 Android、網頁以及桌面 建立遊戲。這可以節省您的時間和精力,並讓您的遊戲從第一天起就能接觸到更廣泛的受眾。

最後,Flutter 遊戲載入速度快,並且通常效能非常好,即使是在低端設備或瀏覽器中也是如此。捆綁包大小可以更小,因為 Flutter 引擎只會為您的遊戲增加幾兆位元組。

更新後的 Flutter 休閒遊戲工具包

自 2022 年發佈工具包的第一個版本以來,我們看到了 Flutter 社群的熱烈反響,但我們也收到了強烈的回饋,表明您需要更多開發成功遊戲的資源和指導。因此,我們正在使用新的遊戲範本和資源更新 Flutter 休閒遊戲工具包,以幫助您在 Flutter 中輕鬆建立休閒遊戲。僅僅列舉幾個更新:

多種類型遊戲的範本

我們新增了三個新的遊戲範本,為建立休閒遊戲提供起點。首先,您會找到一個 基本範本,它具有用於主選單、設定螢幕、簡單關卡選擇和音訊整合的簡單 UI。

一個展示基本範本的動畫 GIF

其次,您會找到一個 卡牌遊戲範本,它建立在基本範本之上,並新增了基本的卡片拖放功能,並已準備好整合多人遊戲。

一個展示卡牌範本的動畫 GIF

第三,我們與開源遊戲引擎 Flame(基於 Flutter 建造)合作,提供了一個 無限跑酷範本。雖然您已經可以使用 Flutter 來建立許多回合制遊戲,例如卡牌遊戲、文字謎題和棋盤遊戲,這些遊戲對簡單的使用者輸入做出反應,但您可能也想要建立實時遊戲,例如無限跑酷、射擊遊戲和賽車遊戲,這些遊戲需要遊戲循環。在這些情況下,您可以使用 Flame 來實作遊戲方向、碰撞偵測、視差、生成和不同的視覺效果。

一個展示無限跑酷範本的動畫 GIF

若要使用這些範本,請查看 儲存庫,並使用最符合您需求的範本開始建立遊戲。

遊戲服務整合的開發者資源

工具包包含用於整合必需服務的開發者指南,以使您的遊戲更成功。例如,我們包含了幾個新的食譜和 codelab,以向您展示如何:

Flutter 休閒遊戲工具包,與 Flutter 一樣,是免費的(BSD 3 授權)且開源的。您可以在 flutter.dev/games 上了解更多資訊並開始使用。我們希望透過更多類型專用的範本和開發者資源,為工具包發佈進一步的更新。請發送電子郵件至 [email protected] 告知我們還有哪些可以讓您的遊戲開發體驗更輕鬆!

在 Flutter 中開始建立您的遊戲

我們很興奮地看到您使用 Flutter 休閒遊戲工具包建立的內容。無論您是經驗豐富的遊戲開發者還是剛剛起步,我們都擁有您創建出色的休閒遊戲所需的工具和資源。您還在等什麼?今天就開始建立吧!


使用 Flutter 建立您的下一個休閒遊戲 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

開發大型螢幕的 Flutter 應用程式

去年,我們與 gSkinner 團隊合作開發了 Wonderous,這是一個參考應用程式,用於展示 Flutter 可以實現的高品質體驗。

建立 Wonderous 的目標之一 是提供一個開源的範例,展示最佳實務。本著同樣的精神,我們根據 Android 大型螢幕指南 審查了 Wonderous。我們希望確保在所有 Android 設備上提供高品質的 Flutter 應用程式。Android 有三個層級來滿足其指南:

第三層級 - 大型螢幕就緒 - 應用程式在全螢幕(或多視窗模式下的全視窗)中運行,但應用程式佈局可能不理想。

第二層級 - 大型螢幕優化 - 應用程式佈局針對所有螢幕尺寸進行優化,並處理外部輸入設備。

第一層級 - 大型螢幕差異化 - 應用程式專為摺疊裝置而設計,確保佈局支援具有鉸鏈的桌面模式。

2023 年 1 月,我們更新了 Wonderous 應用程式以適應不同的設備格式。雖然這些更新使應用程式「大型螢幕就緒」,但應用程式尚未「大型螢幕優化」。

因此,我們再次與 gSkinner 合作,將 Wonderous 提升到第二層級(或更高)並針對 Android 的全新大型螢幕 Pixel 設備 進行優化。

在本文中,我們將分享我們的學習心得,以及您在開發應用程式以滿足 Android 大型螢幕指南時應該考慮的因素。

大型螢幕為何重要

在深入探討我們如何優化 Wonderous 以滿足 Android 大型螢幕指南之前,讓我們先了解支援大型螢幕的好處。

當我們說 大型螢幕 時,我們指的是平板電腦、摺疊裝置和運行 Android 的 ChromeOS 設備。對大型螢幕的需求正在增加。如今,超過 2.7 億台活躍大型螢幕 和摺疊裝置在 Android 上運行。

這對開發人員很重要:

  • 提高業務指標,例如,對佈局優化為填滿全螢幕的應用程式來說,平板電腦上的使用者參與度提高。
  • 提高 技術品質,以提高您的應用程式在 Play 商店中這些設備上的可見度。最近的 Play 商店更新 顯示了按設備類型劃分的評分,並且在應用程式缺乏大型螢幕支援時會通知使用者。

從優化 Wonderous 中的學習心得

Wonderous 應用程式的另一個目標是作為建立學習材料和文件的來源。在本節中,我們將分享 Wonderous 未能滿足大型螢幕要求的地方。這些問題可能是 Flutter 開發人員遇到的常見問題,因此我們將分享我們的解決方法。

應用程式延續性與設定變更

要求: 應用程式應隨著設備旋轉、變更視窗大小或摺疊和展開而變更方向,並保持或還原狀態。

問題:摺疊/展開導致狀態丟失

當 Wonderous 被摺疊和展開時,應用程式會遇到狀態丟失,並且始終會返回到初始螢幕。這個問題讓我們花費了很長時間才弄清楚。我們最初認為這是 Flutter 在摺疊/展開設備時預設的行為,但無法使用 Flutter 計數器應用程式重現它。然後我們意識到可能是某個外掛導致狀態丟失,特別是 flutter_inappwebview 外掛。

解決方法: 將 flutter_inappwebview 外掛替換為 webview_flutter,這解決了在設備設定期間保持狀態的問題。

學習心得: 您的應用程式在設備摺疊/展開時不應丟失狀態,Flutter 預設應保持狀態。如果您的應用程式正在丟失狀態,請確認您使用的外掛支援大型螢幕設備。原生擴展可能會在設備位置變更時導致狀態丟失。

問題:保持捲軸位置狀態

我們認為我們可能需要很多重複的程式碼來確保清單在螢幕尺寸變更時保持其捲軸位置。我們首先使用了 key:PageStorageKey API,它修復了我們的大部分清單視圖。「學習心得」部分描述了您需要進一步操作的情況。

Wonderous 應用程式中的集合頁面未保持捲軸位置

解決方法: 使用 key: PageStorageKey API 來保持清單的捲軸位置,這些清單在設備方向變更時保持相同的佈局。透過 為集合頁面新增 PageStorageKeySingleChildScrollView Widget 的捲軸位置將會 自動儲存

學習心得: 雖然 key: PageStorageKey 提供了一種優雅的方式來使用清單保持捲軸位置,但如果您的清單在設備方向變更時從垂直變更為水平,它將不起作用。以前的 GIF 顯示了 Wonderous 的某些頁面發生了什麼。在這種情況下,您可能需要 進行一些數學運算並在螢幕旋轉時變更捲軸位置

多視窗與分割畫面

要求: 應用程式在多視窗模式下具有完整的功能。例如,當應用程式沒有最高焦點時,應用程式會更新其 UI,繼續播放媒體。

問題:由於分割畫面視圖導致 Letterboxing

隨著 「螢幕分割」 的出現,使用者可以(在某些方向和縱橫比下)將應用程式置於不到兩英寸的垂直空間中。當螢幕被分割到顯示區域的 ⅓ 時,Wonderous 應用程式被 Letterboxed 或放置在顯示區域的中心,實心色背景填補了未使用的區域(如下所示)。

Wonderous 應用程式在 Pixel Fold 上的分割畫面模式下被 Letterboxed

這是因為我們選擇透過使用 MediaQuery 來確定設備的大小,在較小的螢幕上禁用橫向模式。不幸的是,MediaQuery 在您處於分割畫面模式時實際上無法提供螢幕尺寸,並且錯誤地將具有分割畫面的大型設備識別為小型設備。

解決方法: 我們在 Flutter 中加入了一個新的 API,為 Android 的新視窗尺寸類別 提供支援。透過切換到 Display API,我們能夠獲得全螢幕尺寸,並準確區分具有多個視窗的小型設備和大型設備。使用此 API,您可以擷取顯示器的各種屬性。新的 FlutterView.display getter 會返回一個 Display 物件。Display 物件會報告顯示器的實體尺寸、設備像素比和更新頻率。若要查看使用新 API 的範例,請訪問 setPreferredMethod API

學習心得: 在禁用小型設備上的橫向模式時(使用 SystemChrome.setPreferredOrientations()),請使用 Display API 來確定設備尺寸,而不是 MediaQuery。

鍵盤、滑鼠和觸控板輸入

要求: 應用程式應支援基本的滑鼠或觸控板,以及鍵盤捷徑。主要的使用者流程應支援鍵盤導航,以確保無障礙。

問題:焦點狀態

Flutter 的 Material 按鈕預設會處理基本的焦點狀態。在 Wonderous 的情況下,我們變更了 Material 按鈕的預設樣式,將 overlayColor 設定為透明。這最終破壞了按鈕的焦點狀態。對於具有鍵盤和觸控板的大型螢幕設備,您的應用程式必須處理導航,並為這些週邊設備提供無障礙解決方案。

解決方法: 變更按鈕樣式,以便在按鈕獲得焦點時顯示輪廓。我們使用了 FocusNodehasFocus 屬性 來檢查按鈕節點何時有焦點輸入,並在按鈕中加入了邊框。

學習心得: 確保為應用程式中的任何自訂按鈕或手勢偵測器實作焦點狀態。如果您變更了預設的 Material 按鈕樣式,請確保您正在測試鍵盤焦點狀態,並根據需要實作自己的焦點狀態。

問題:滑鼠捲軸速度慢

滑鼠滾輪在應用程式的某些設備上效果不佳。使用者必須大幅度移動滾輪才能使螢幕響應。這是一個已知的 問題,感謝我們的社群。我們注意到在平板電腦和摺疊裝置上這種情況更加明顯。

使用滑鼠捲動 Wonderous 應用程式速度很慢

解決方法: 我們在框架中 修復了這個問題,因為它影響了所有 Flutter 應用程式,並且將在下一版 Flutter 穩定版本中提供。

學習心得: 有時問題不是您的應用程式獨有的,需要 Flutter 團隊來修復 :)

應用程式使用者體驗佈局

要求: 應用程式應提供自適應佈局和響應式視覺元素,這些元素會根據可用螢幕空間或設備方向進行變更。

問題:導航軌

在 Wonderous 的第一個版本中,我們禁用了應用程式在小型外形尺寸上的旋轉,因為應用程式在較小的螢幕上缺乏適當的橫向模式支援。如前所述,我們修正了應用程式,以便在大型螢幕設備上分割畫面模式下,小型螢幕能夠支援橫向模式。雖然這種方法修復了 Letterboxing 問題,但應用程式的導航缺乏大型螢幕的適當人體工學變得更加明顯。

在使用 Material 的導航軌之前

這要求應用程式將導航佈局在螢幕邊緣附近,這些邊緣更容易觸及,例如平板電腦的左側邊緣,以在橫向模式下為應用程式內容釋放寶貴的垂直空間。

解決方法: 更新設計,並實作類似於 Material 的導航軌 的導航。

學習心得: 確保應用程式中的佈局和導航在大型螢幕設備和約 2 英寸的垂直空間(如果螢幕處於分割模式)中可以正常運作。為了使這成為可能,請使用 自適應 Widget,例如 Material 的導航軌。

使用 Material 的導航軌之後

其他事宜

問題:相機預覽和媒體投影

要達到 Android 的第三層級 狀態,您的應用程式需要在不同的方向、螢幕尺寸和多視窗模式下支援相機預覽和媒體投影。由於 Wonderous 沒有相機功能,因此此要求不適用。

學習心得: 顯示相機預覽 令人驚訝地複雜,但 Jetpack CameraX 函式庫 處理了其中許多細節。在 Flutter 3.10 中,我們在 Flutter 相機外掛中加入了對 CameraX 的初步支援。若要嘗試它,請透過在您的 pubspec.yaml 檔案中加入以下行來選擇加入。

1
2
3
Dependencies:
camera: ^0.10.4 # 最新相機版本
camera_android_camerax: ^0.5.0

不僅限於 Android

雖然這些修復和學習心得是針對 Android 大型螢幕設備提出的,但某些可能適用於其他平台。它們可以讓您的應用程式在 iPad、桌面瀏覽器和桌面設備上運行得更好。同樣的學習心得也適用:利用 PageStorageKey API、使用自適應 Widget 和佈局,或確保您使用的外掛能與大型外形尺寸配合良好。

總結

我們優化 Wonderous 應用程式以滿足 大型螢幕 需求的主要目標是展示開發 Flutter 應用程式的最佳實務,這些應用程式可以為所有 Android 設備提供絕佳的體驗。

在滿足 Android 的 第二層級狀態 的全面要求的過程中,我們發現了如何修復其他在大型螢幕上運行的 Flutter 應用程式中可能遇到的常見問題。以下是一些主要的學習心得:

  • 在變更設備方向或摺疊/展開設備時,保持並還原 應用程式狀態
  • 透過使用 Display API 準確確定螢幕尺寸,確保您的應用程式沒有被 Letterboxed。
  • 處理外部 輸入設備,例如使用滑鼠或鍵盤捲動。
  • 專門針對大型螢幕設備設計您的應用程式佈局,例如利用 Material 的導航軌

您可以在 Android 的開發人員網站上進一步了解 Android 的 大型螢幕應用程式品質 指南。

我們希望您在開發自己的 Flutter 應用程式以滿足 Android 的大型螢幕要求時,可以將 Wonderous 作為參考。

如果您對應用程式有任何回饋,請 在 Github 上提交議題。如果您對 Flutter 的 Android 支援或支援大型螢幕設備有任何想法,請隨時在 X (@marihasnany) 上向我發送訊息。


開發大型螢幕的 Flutter 應用程式 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

企業可以到哪裡找到顧問來協助打造下一個應用程式?

設計公司和數位科技顧問在應用程式的成功中扮演著至關重要的角色。企業通常會尋求顧問的協助,以幫助他們建立概念驗證、重新設計現有應用程式,或作為開發專案的專家。新創公司則會尋求代理商來建立品牌識別和製作最小可行產品原型。這些值得信賴的合作夥伴可能很難找到。

因此,我們很興奮地推出 Flutter 顧問名錄

flutter.dev/consultants

我們推出這個新頁面,簡化尋找值得信賴的 Flutter 合作夥伴的流程。我們會審查名錄中列出的合作夥伴,以驗證他們在 Flutter 專案上的成功記錄、充足的技術熟練開發人員,以及他們對發展 Flutter 社群的承諾。

顧問的資源

作為此次推出的部分,我們將重點介紹一些資源,以幫助潛在的顧問提升他們的 Flutter 專業知識。這包括:

Flutter 簡報範本

複製一份並加入您團隊的案例研究,向潛在客戶提供有說服力的 Flutter 案例。

點擊 [連結](https://docs.google.com/presentation/d/1TblnxXMVO1c75ggdLnA4cPh_nsDVvT--RsFqUIN6Fvg/copy) 自行複製一份。

用 Flutter 打造奇蹟

來自頂尖 Flutter 設計公司 gSkinner 的見證,說明為什麼 Flutter 對他們的團隊來說是一個寶貴的工具箱。

FlutterFlow 如何讓顧問更快地完成工作

了解顧問如何使用 FlutterFlow 更快地交付高品質的客戶專案。 閱讀部落格文章。_

IBM 如何建立其 Flutter 優秀中心

了解 IBM 為什麼以及如何用數百位 Flutter 開發人員建立 Flutter 優秀中心。 閱讀部落格文章。_

訓練客戶使用 Flutter

了解 VGV 如何建立成功的 Flutter 訓練課程。您可以提升自己的開發人員或客戶的技能。 閱讀部落格文章。_

準備好申請了嗎?

要申請,代理商和顧問應該填寫 這個 Google 表單。我們會定期審查提交的內容,並會在適當的時間通知您申請狀態。我們要求顧問每季更新其資訊,以確保名錄的準確性和相關性。

有任何回饋嗎?

我們總是樂於接受回饋。如果您對如何改進名錄或合作夥伴資源有任何想法,請發送電子郵件至 [email protected]


介紹 Flutter 顧問名錄 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

了解 IBM 如何透過 Flutter 助客戶成功

有效的客戶合作包含兩項關鍵因素:資源的明智運用和及時的交付。

在當今以行動裝置和數位為先的環境中,客戶對體驗、速度、個人化和便利性的期望呈指數級增長。透過使用 Flutter,開發人員和設計師可以更快、更有效地建立跨平台的網頁和行動應用程式。

我們為何要擴展 Flutter 的實務

Flutter 幫助我們更快地交付客戶專案

透過從單一程式碼庫中針對多個平台進行開發,我們的團隊可以比以往更快地進行原型設計、改進和部署客戶應用程式。使用 Flutter,我們可以將專案交付速度提高三倍,而不會犧牲品質。

在 COVID-19 爆發初期,西班牙比斯開省政府聯繫 IBM,希望獲得協助,向其公民傳達最新行動和消息。他們需要一個行動應用程式,而且需要在兩週內完成。透過使用 Flutter,應用程式的第一個版本在短短 11 天內就發佈了。它迅速成為該地區下載量第三高的應用程式。

“在如此緊迫的時間限制下,Flutter 成為我們的救星。這是我們做出的最快的困難決定,也是我們做出的最相關的決定之一。為 iOS 和 Android 建立應用程式就如同為我們的開發團隊賦予超能力,讓我們在與時間賽跑中獲勝。”
— IBM 專案總監 Joanra Mallart

Flutter 幫助我們的客戶以更少的資金獲得更多

交付絕佳的使用者體驗始終是我們首要考量。然而,許多客戶的預算很緊,這往往需要放棄會幫助其應用程式成功的平台或功能。透過使用 Flutter,我們能夠透過無妥協的開發來超越客戶的期望。

客戶正在詢問 Flutter

Flutter 的普及程度不斷提高,這從最近的 StackOverflow 報告 中可以看出,該報告將其列為最受歡迎的 UI 框架。因此,我們看到越來越多的客戶向我們詢問如何在他們的應用程式中使用 Flutter。開發人員和業務利益相關者都對利用 Flutter 的優點感興趣。

與 Google 的無縫協作

作為 Google Cloud 的主要合作夥伴,IBM 和 Google 已經建立了緊密的關係。透過採用 Flutter,我們能夠利用開發人員產品之間的無縫整合,並在需要時獲得 Google 團隊的支援。

我們如何建立卓越中心

數十年來,IBM 一直處於以使用者為中心的行動創新的最前沿,由 IBM iX 領導,IBM iX 是 IBM 諮詢的體驗和設計合作夥伴。我們在全球 60 個 IBM iX 工作室中擁有超過 15,000 名開發、設計和策略實務者,為全球客戶提供傑出的行動體驗。

然而,對於一個龐大且分散的團隊來說,建立一個知識共享的單一場所至關重要。建立卓越中心有助於我們的團隊推薦最佳實務、重複利用經驗教訓,並在整合的社群中慶祝客戶的成功。

以下是我們為建立 IBM 諮詢的 Flutter 開發和設計卓越中心而設定的優先事項:

1. 定期舉辦社群論壇

在 IBM,我們每季度舉辦一次 Flutter 社群電話會議。在這個電話會議中,開發人員和設計師可以分享客戶參與的經驗教訓、討論 Flutter 和 Dart 的新功能,以及從更廣泛的 Flutter 社群中獲得更新。

2. 建立一個提問的環境

我們建立了一個專用的聊天室,讓開發人員和設計師可以隨時提出與 Flutter 相關的問題。我們有一群 Flutter 專家可以協助解決問題或推薦特定方法。

3. 將知識共享和可重複使用的組件集中化

許多客戶專案在架構上存在相似之處。因此,我們建立了簡單的專案範本,實務者可以從中提取這些範本以加快開發速度。

4. 為客戶建立教育材料

雖然許多客戶了解 Flutter 的優點,但有些人可能想要更好地了解 Flutter 如何幫助 _他們_。基於 Flutter 推介資料,我們開發了材料來教育客戶了解 Flutter 與其他框架的不同之處。我們建立了一個客戶案例研究的中心儲存庫,以便輕鬆展示 Flutter 如何幫助以前的客戶。

5. 為 Flutter 實務者建立學習路徑

在 IBM,實務者可以獲得特定技術或領域的認證。我們建立了一個專用的學習路徑,幫助開發人員學習 Flutter 並將這些知識應用於新的客戶參與中。當實務者完成此路徑後,他們將在我們的資料庫中被記錄為 Flutter 實務者 - 因此可以輕鬆快速地為 Flutter 專案配備人員。

透過建立這個卓越中心,我們幫助實務者加快了他們對 Flutter 的學習。藉由這些資訊,我們可以更好地、更快地交付客戶專案。

未來:推動 Flutter 的卓越

到目前為止,IBM 在交付 Flutter 專案方面取得了重大成功。我們的客戶包括全球銀行、聯邦和州政府以及國際軍方人員。我們還在全球範圍內交付了許多概念驗證。

憑藉 Flutter 的靈活性以及 IBM 諮詢的創新傳統,我們正在努力塑造出色的行動體驗,讓企業及其客戶產生共鳴。隨著我們擁抱這個新的合作階段,我們邀請您加入我們這個轉變之旅。

立即聯繫我們,了解 IBM 諮詢 如何利用 Flutter 的速度、效率和卓越來賦能您的業務。

John Mutter 是 IBM 全球 CTO 體驗工程師,也是 IBM 傑出工程師。


IBM 如何建立 Flutter 卓越中心 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

Flutter 3.13 的新功能:2D 捲軸、更快的圖形、Material 3 更新等等

歡迎回到我們的季度 Flutter 穩定版本發布,這次是 Flutter 3.13!在我們上一次版本發布後的短短三個月內,我們已經合併了 724 個 pull 請求,並且有 55 位社群成員為 Flutter 貢獻了他們的第一次提交!

繼續閱讀,了解 Flutter 社群為這個最新版本貢獻的所有最新功能和改進!

引擎

我們對 Impeller(我們新的圖形渲染器)進行了一些改進,並為可折疊設備添加了新的引擎 API。

Impeller

iOS 效能改進

感謝 Flutter 用戶的高品質回饋,在此版本中,我們繼續改進 Impeller 在 iOS 上的效能。由於許多不同的優化,Impeller 渲染器在 iOS 上現在不僅具有更低的延遲(透過完全消除著色器編譯卡頓),而且在某些基準測試中還具有更高的平均吞吐量。特別是在我們的 flutter/gallery 轉場效能基準測試中,平均畫面柵格化時間現在大約是 Skia 的一半。

iPhone 11 上的 Flutter Gallery 轉場效能基準測試中平均畫面柵格化時間的改進。涵蓋的時間段大約是從 3.10 分支切換到 3.13 分支的時間。

這些進展要歸功於這些和其他優化,包括:

保真度改進

在 3.10 中,我們宣布在使用 Impeller 時,廣色域色彩在標記下可用。在聽取並解決了用戶的回饋後,廣色域色彩現在是使用 Impeller 時 iOS 上的預設值。

Impeller 在 Android 上的進展更新

我們繼續在 Impeller 的 Vulkan 後端取得進展,但它尚未達到可以使用官方預覽期的品質水準。我們希望確保用戶在 Android 上第一次使用 Impeller 的體驗是高品質的,而我們還沒有達到這個水準。我們希望在今年晚些時候的穩定版本中進入 Android 上 Impeller 的預覽期。儘管 Android 上的 Impeller 尚未準備好進行預覽,但 OpenGL 和 Vulkan 後端從我們在過去一年中對 Impeller 的 HAL 進行的許多後端無關優化中受益。特別是在 flutter/gallery 轉場效能基準測試中,Android 的平均畫面柵格化時間也顯著提高。正在進行進一步的改進,以便 Android 上的預覽可以達到高品質。

再次感謝社群的貢獻,特別是 GitHub 用戶 ColdPaleLight,他撰寫了幾個非常受歡迎的與 Impeller 相關的修補程式,改進了保真度和效能,包括添加了 對圓錐形漸變的支援

請繼續使用 Impeller 的 專案儀表板 在 GitHub 上追蹤我們的進度。我們非常感謝所有回饋,並鼓勵用戶繼續在 問題追蹤器 中提交保真度和效能問題。

Impeller(以及 Wonderous)在 macOS 上

在我們上次穩定版本發布中,我們宣布 Impeller(我們渲染引擎的重寫版本)將在 iOS 上預設啟用。從那以後,我們聽到了客戶的良好回饋。現在,我們很興奮地宣布 macOS 的 Impeller 已在預覽版中提供。您可以透過遵循 Impeller 頁面 上的指南來測試 Impeller 並在您的應用程式中啟用它。

我們渴望您測試並提供回饋。幫助我們改進 macOS 的 Impeller 的最佳方法是透過在沒有 SkSL 預熱的情況下運行您的 macOS 應用程式,並使用 DevTools 找出由於著色器編譯導致 卡頓 的實例,來建立基準指標。接下來,使用 Impeller 測試您的應用程式 - 點擊並檢查錯誤、效能改進或效能回歸。如果您發現任何問題,我們強烈建議您在 GitHub 上提交它們。請務必包含您正在運行的設備資訊、影片錄製以及 效能追蹤導出的檔案

想要在 macOS 上嘗試 Impeller 嗎?從 Mac App Store 安裝 Wonderous

新的引擎 API

改進的可折疊設備支援

為了更好地支援可折疊設備,我們添加了一個新的 API 來擷取顯示器的各種屬性。新的 getter FlutterView.display 返回一個 Display 物件。Display 物件報告顯示器的物理尺寸、設備像素比和更新速率。查看 setPreferredOrientations 以獲取使用新 API 的範例。

架構

Material

我們在 Material Framework 中進行了一些改進,以 1) 提供更多平台適應性,2) 允許更多自訂,以及 3) 添加新功能。

TextField 中的字元辨識

在 iOS 上使用 TextField 時,使用者會自動看到一個選項,可以使用裝置相機來辨識字元並將它們插入到欄位中。

這個功能要感謝社群成員 luckysmg(作者)和 tgucio(審查者)的貢獻。這個功能是一個擁有 1000 行程式碼和 70 次提交的努力,它連接了 引擎框架!謝謝!

平台自適應對話方塊

已在 AlertDialog 中添加了一個自適應建構函數,以及自適應函數 showAdaptiveDialog,根據當前平台顯示 Material 或 Cupertino 對話方塊。

現在使用 AlertDialog.adaptive() 在 iOS 上使用 CupertinoAlertDialog Widget:

以及在 Android 上的 Material AlertDialog

具有月份和年份的 CupertinoDatePicker

為 CupertinoDatePicker 添加了一個 monthYear 模式。

Cupertino(iOS 風格)檢查樣式的單選按鈕

已在 CupertinoRadio 中添加了 useCheckmarkStyle 屬性。這也允許 Radio.adaptive 和 RadioListTile.adaptive Widget 控制它們是否在 iOS 上使用檢查樣式。

Material Widget 的更多自訂選項

有一些改進讓自訂 Material Widget 的設計變得更容易:

  • 您現在可以使用 InputDecoration 的 error 屬性(而不是字串)來自訂顯示在文字欄位上的錯誤 Widget:
  • 您現在可以為 ButtonSegment 添加工具提示:
  • 您現在可以使用 materialGapSize 屬性自訂 ExpansionPanelList 中的間隙:
  • 您現在可以自訂 Switch 的 trackOutlineWidth:
  • 您現在可以使用 NavigationDrawer 上的 tilePadding 屬性自訂內距:
  • 您可以使用 TabBar 的 alignment 屬性選擇如何對齊標籤:

MaterialState 顏色用於晶片

這個 使得能夠在所有不同的狀態下自訂晶片的顏色。

凸顯的晶片

根據 Material 3 規格,已添加了 FilterChip.elevated、ChoiceChip.elevated 和 ActionChip.elevated 變體。

允許在使用者完成文字輸入並按下鍵盤上的完成按鈕時啟動不同的操作。

BaseTapAndDragGestureRecognizer

已為一組類別添加了一個基類,其中包括對點擊 + 拖曳(TapAndPanGestureRecognizer)和點擊 + 水平拖曳(TapAndHorizontalDragGestureRecognizer)的支援。這些類別已經用於實作原生文字欄位手勢。但是,它們也適用於其他用例 - 例如,使用雙擊 + 垂直拖曳手勢來縮放 Widget。

應用程式生命週期變更

AppLifeCycleListener

添加了 AppLifecycleListener 類別,用於偵聽應用程式生命週期的變化並回應退出應用程式的請求。

捲軸

二維捲軸基礎

此版本的 Flutter 還包含建立在兩個維度中捲軸的 Widget 的基礎,這意味著可以使用的許多新類別,包括:

在兩個維度中捲軸還帶來了新的互動,包括對角線捲軸。查看 DiagonalDragBehavior 以獲取新的互動類型,並在您的 TwoDimensionalScrollView 或 TwoDimensionalScrollable 上配置它們。

我們進行了一項用戶研究,以便為開發人員建立這個基礎,使他們能夠在所有方向捲軸時建立他們可以想像的任何東西。查看在 這個 DartPad 中實作的簡單、延遲加載的二維網格的範例,大約 200 行程式碼!

Flutter 團隊已經在努力在這個框架之上建立二維捲軸 Widget,即將在 two_dimensional_scrollables 套件中推出。

新的 Sliver

Flutter 3.13 帶來了一組新的 Sliver,用於組合獨特的捲軸效果。

SliverMainAxisGroupSliverCrossAxisGroup 都支援將多個 Sliver 安排在一起。在主軸中,這可以創造出一個效果,就是粘性標題,允許固定的標題在每個 Sliver 組在視圖中捲動時被推離視圖。

橫軸分組允許 Sliver 在視圖中並排排列,以及(同樣新的)Widget,例如 SliverCrossAxisExpandedSliverConstrainedCrossAxis,能夠確定橫軸中每個分組 Sliver 的空間分配。

Sliver 函式庫中新增的還有 DecoratedSliver,類似於 DecoratedBox。這允許用戶使用 Decoration 來修飾 Sliver,甚至可以是 Sliver 組。

這個 DartPad 範例中查看所有這些新的 Sliver 的實際操作。

無障礙功能

無障礙功能更新

  • 為 CupertinoSwitch 添加了 onOffSwitchLabels 無障礙功能屬性,以顯示 I/O 標籤

平台

Android

新的支援目標

有了這個版本,Flutter 現在支援針對 Android 14/ API 34 進行目標設定。雖然我們仍在為 Android 14 中的一些新功能努力(例如預測性後退導航),但我們已針對新的 Android SDK 徹底測試了這個版本,並優先解決您可能遇到的任何相關問題。

iOS

iOS 上減少旋轉失真

當 iOS 螢幕旋轉時,Flutter 應用程式以前會遇到一些失真,看起來與原生 iOS 應用程式不同。我們進行了一些修改以減少 失真

重新命名 Runner

當創建 Flutter iOS 應用程式時,會在 /ios 文件夾中創建一個 Runner Xcode 專案和 Xcode 工作區。現在,您可以重新命名工作區或專案,這樣您就不會得到一堆 Runner。

準備好迎接 iOS 17 和 Xcode 15

隨著 iOS 17 和 Xcode 15 的即將發布,想要使用這個工具鏈進行開發的用戶需要使用 Flutter 3.13。此外,在下載 Xcode 15 時,請務必也下載 iOS 17 模擬器。

遊戲

Flutter 遊戲更新

我們在 2022 年推出了 Flutter 休閒遊戲工具包,其中包含遊戲範本、教程、文件、社群空間以及 GCP/Firebase/Ad 積分,以幫助 Flutter 開發人員快速開始遊戲開發。從那以後,已經使用 Flutter 發布了數萬個遊戲!自發布以來,我們一直在積極與 Flutter 遊戲開發人員互動和調查,以了解如何改進遊戲工具包。幾乎所有開發人員都提到了想要更多資源和範例程式碼,以幫助他們更好地設計、開發和變現他們的遊戲。

今天,我們將發布對 Flutter 遊戲網頁 的最新更新,其中包含一個輪播視圖,用於展示在 Flutter 中構建時可以學習的影片資源和新遊戲。在接下來的幾個月裡,我們將為工具包提供一些新的更新,其中將提供額外的資源和樣本,以幫助您開始遊戲開發之旅。

作為第一步,我們於 2023 年 7 月與 AdMob 合作,共同舉辦了一場專為 Flutter 遊戲開發者提供的獨家 UX 設計和變現研討會。大約 100 位開發人員透過現場互動式網路研討會加入了我們,並且給予了該研討會 4.6/5.0 的滿意度評分。我們希望能夠很快總結研討會的內容,並與大家分享這些見解。

我們正在積極努力更新,敬請期待!如果您已經在使用遊戲工具包,並且想要向我們提供未來改進的想法,請隨時發送電子郵件到 [email protected]

工具

DevTools

新的 DevTools 功能

我們對 DevTools 的效能和可用性進行了一些改進,包括:

我們在導航欄上添加了一個新的溢出選單,用於處理當標籤列表無法一次顯示時的情況。

我們在記憶體標籤上添加了一個類別類型的圖例。

此外,我們還使在 CPU 分析工具中捲軸樹表格變得更快、更流暢。在除錯工具中,我們已使在檔案中搜索或搜索檔案的速度提高了 5 倍。

若要進一步了解,請查看 DevTools 2.25.0DevTools 2.24.0 的發行備註。

重大變更和棄用

重大變更

下一個版本中預設使用 Material 3

我們很興奮地宣布,在 下一個 Flutter 穩定版本中,我們計劃將 ThemeData useMaterial3 預設值從 false 變更為 true。換句話說,應用程式將預設獲得 Material 3 顏色、文字樣式和其他視覺效果。

Material 3 範例 應該有助於預覽 M2 和 M3 之間的差異。

Android 支援平台

Flutter 不再支援 Android Jelly Bean API 層級(16、17 和 18)。好消息是,大多數應用程式應該可以 預設遷移到這個新的 minSdkVersion

但是,如果您沒有自動遷移,可能是因為您對模組級別 build.gradle 進行了更改,您可能需要手動增加 minSdkVersion。若要更新,請從 Flutter 專案的根目錄定位模組級別 build.gradle。它通常位於 /android/app/build.gradle。將 minSdkVersion 版本提升到 19。如果看到 flutter.minSdkVersion 且它至少為 19,那麼您的最小版本設定正確。

Flutter Plugin 默認情況下不會遷移,因此 Plugin 作者應該在位於 /android/build.gradle 的頂級 build.gradle 檔案中更新 minSdkVersion。

變更和遷移指南列表

此版本中的重大變更包括在 v3.10 發布後過期的已棄用 API。若要查看所有受影響的 API,以及其他背景資訊和遷移指南,請參閱 此版本的棄用指南。其中許多由 Flutter Fix 支援,包括 IDE 中的快速修復以及使用 dart fix 命令進行批次應用。

與往常一樣,非常感謝社群為 貢獻測試,它們幫助我們識別出這些重大變更。若要進一步了解,請查看 Flutter 的重大變更政策

貢獻

Flutter 儲存庫的優先順序和分類

分類更新

在過去的幾個月裡,我們採用了一套新的優先順序定義(P0-P3)。這使我們更符合大多數其他開源專案使用的定義,並簡化了我們必須做出的有關錯誤重要性的決策 - 我們現在只有 4 個類別,而不是 7 個。希望這也能幫助我們更有效地與那些提交錯誤並想知道 何時修復 的人交流!

我們還為我們的團隊引入了一個 新的分類方案,我們希望這將使問題更難以遺漏。如果您在我們的問題資料庫中很活躍,您可能會看到我們的全新機器人添加評論以及添加或移除標籤:

這個機器人也與 我們的 Discord 整合,幫助我們掌握 GitHub 上發生的事件。我們希望它能從長遠來看提高我們的生產力,但在我們適應新系統的過程中,請耐心等待!

結語

隨著我們接近這個公告的尾聲,我想承認,沒有我們出色的社群的努力,我們不可能達到今天的成就!

若要查看此版本中包含的所有 PR 的完整列表,請查看此版本的 發行備註和變更日誌 部分。

Flutter 3.13 今天在 stable channel 中提供,並且包含 Dart 3.1。若要開始使用這些最新的更新,只需要執行 flutter upgrade

很快再見!


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

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

Dart 3.1:回顧 Dart 3 中的功能式程式設計風格

模式匹配和窮舉式切換結合在一起,可以啟用與 Dart 的物件導向核心無縫融合的功能式風格資料模型。

Dart 3 重構使用功能式風格特性的內部程式碼庫的 diff

今天我們發佈 Dart 3.1,這是自 5 月份重大 Dart 3.0 發佈以來,我們的第一个穩定版本。Dart 3.1 包含一些次要更新,以及一些 API 調整,以進一步使用 3.0 中介紹的類別修飾符(您可以在 變更日誌 中了解更多資訊)。不過,大多數情況下,我們一直在花時間研究新的路線圖項目,我們希望看到它們在未來的版本中達到 beta 和穩定版本。敬請期待未來關於這方面的更多資訊!

因此,作為傳統發佈文章的替代,我們將回顧 Dart 3 中主要功能的一部分,討論它們如何徹底改變(在某些情況下)以及如何大幅改進您撰寫和組織 Dart 程式碼的方式。

您如何建模資料?

物件導向 (OO) 和函數式語言在許多方面有所不同,但可以說,每種範式如何建模資料 是區分它們的決定性特徵。具體來說,建模相關資料的不同變體以及對這些變體的操作的問題。

「我應該如何建模這些資料?」 通常不是我們在開始新專案時會意識地思考的事情。我們傾向於使用我們所使用語言類型中常見的任何資料建模範式,而不是相反,根據對我們的資料最有意義的模型來選擇語言。

如果您使用的是 OO 語言,您將使用類別階層和對子類別的操作來建模資料。如果您使用的是某些函數式語言,與類別階層模型等效的是 代數資料類型 模型,其對操作子類別的操作等效於使用 模式匹配 在它們之間切換。

物件導向類別階層模型和函數式代數資料類型模型的簡化並列比較

Dart 是一種物件導向語言,但隨著時間推移一直在穩步地整合函數式特性,允許以更多多範式方法進行資料建模。最近,Dart 3 添加了 模式匹配switch 上的新功能,以及 封閉類型。這些特性使在 Dart 中實作出代數資料類型成為可能,讓您可以在繼續最大限度地利用 Dart 的物件導向核心的同時,撰寫功能式風格的程式碼。

像 Dart 這樣的多範式語言為您提供了從單行運算式到整個類別階層的設計工具和機會。您可以考慮哪個模型最適合您的專案,或者僅僅只是考慮您的個人喜好。為了幫助您做出最佳決定,本文將分別總結每個範式的結構和優勢,然後教您如何在 Dart 3 中使用新特性來重構一些經典的物件導向設計,這些設計最適合以函數式風格撰寫。

物件導向方法

當您針對不同的資料類型具有特定操作時,在 OO 語言中,標準組織方法是在基類上建立一個方法,以及一組覆寫基類以定義其唯一行為的子類別。每個子類別都將其資料和操作放在其宣告中的一個位置。

以這個(高級偽程式碼)建模食譜的範例來說。將食材和步驟與食譜結合在一起,擁有食譜物件是有意義的。食譜基類可能具有用於烹飪方法的一些函數,每個食譜都會覆寫這些函數,以滿足其唯一的需求:

具有實例方法的類別階層使您能夠輕鬆地新增新的子類別,而無需觸摸任何現有程式碼。這適用於某些領域,例如 Flutter,您在其中有數不盡的 Widget 都擴展了 Widget 類別。每個 Widget 都可以唯一地擴展和覆寫其定義中的任何必要行為。您 絕對 不需要知道每個 Widget 子類型如何定義其方法來新增專門的行為到您的自己的 Widget。

函數式方法(代數資料類型)

您可以將函數式風格架構視為 OO 架構的逆向。與將 一種類型的所有程式碼放在一個位置(OO 實例方法在子類別宣告中)不同,您將 一個操作的所有程式碼放在一個位置(函數式切換類型以定義行為)。

這引發了一個問題,什麼時候 有必要 知道階層中每個子類型如何定義操作?可能基於以下幾個原因:

  • 當在程式碼中並排新增、維護和理解同一操作行為的變體更容易時。
  • 當您無法修改子類別本身,但又想定義特定於每個子類別的新行為時。
  • 當操作行為對於不同類型的變體之間的關係比它們與它們所操作的類型之間的關係更緊密時。

有時這很明顯,但大多數情況下隻是一種觀點轉變。再次思考食譜範例。從某種意義上說,例如烤箱手冊,將烤箱說明按每個食譜分組到一個位置更有意義:

在此範例中,程式結構側重於 bake 操作。無論 bake 對哪些類型進行操作,它們都隻是同一函數的不同可能的輸出;bake 與其操作的類型無關。

這就是 代數資料類型模型(以數學集合論中的「代數」命名)。它是函數式語言的核心組織模型,如同類別階層是 OO 語言的核心一樣。代數資料類型透過按操作將所有類型的行為分組在一起,來將行為與資料分開。

現在,可以使用 Dart 3 協調一致地實作出代數資料類型!

建模物件導向代數資料類型

函數式語言通常透過對 總和類型 中的案例進行模式匹配來實作出 代數資料類型 ,以將行為分配給每個變體。Dart 3 在 switch 案例中使用 模式匹配 來實現同樣的效果,並利用了 OO 子類型自然地建模總和類型的特性。這使我們可以使用與 Dart 無縫融合的物件來實作真正 多範式代數資料類型

以下各節將向您展示如何在 Dart 中設計代數資料類型模型,以及使用前 Dart 3 的相同功能的範例。

  • 首先,我們將說明如何透過在 物件模式 上進行切換來將操作的基於類型的變體分組在一起。
  • 然後,我們將退一步,看看如何使用新的 封閉 類別修飾符來設計子類別本身,以確保 switch 為 物件可能採用的所有可能子類型 定義行為。

在類型之間分組行為

Dart 語言的個別部分(例如語句、類別和文字)在類別階層中都有自己的定義,但都受到多個系統(例如解析器、格式化程序和編譯器操作)的操作影響。想像一下,如果每個應用於每個語言元素的函數都必須在這些元素的宣告中定義,語言實作將會變得多麼混亂!它看起來會像這樣:

出於這個原因,Dart 的內部程式碼已經自然地傾向於將函數與類型定義分開的功能式方法。以 Dart 編譯器中的 annotation_verifier 函式庫為例。它包含定義注釋行為的函數(例如 @override 或 @deprecated),具體取決於注釋所附加的程式碼部分(例如 @override 如何影響類別與欄位)。

但根據類型分配行為不像一開始做出將行為分開的決定那麼簡單。根據類型定義行為的標準方法是使用鏈式 if-else 語句,您在注釋驗證器中經常會看到。以以下未使用任何 Dart 3 特性撰寫的驗證函數為例。它驗證了 最近貢獻的 @visibleOutsideTemplate 注釋的行為,該注釋選擇退出另一個注釋 @visibleForTemplate 的級聯效應:

該函數使用精心設計的鏈式 if-else 語句,測試注釋的祖父母是否為某種宣告(ClassDeclaration、EnumDeclaration 或 MixinDeclaration),然後根據類型定義其行為。

使用 Dart 3,您可以在 switch 案例中使用物件模式將此結構顯著地重構為更宣告式的風格,使其更短、更容易閱讀。原始作者 就是這麼做的!16 行鏈式 if-else 語句被精簡為 7 行 switch 語句:

這裡的每個案例都是一個 物件模式,它與 grandparent 的靜態類型進行比對。與說 if (object is Type && object.property != null) 不同,每個案例都檢查物件的模式是否與模式 Type(propertyOfType) 相符。此外,當物件與物件模式相符時,它隱含地要求它不為 null,因此無需進行顯式空檢查!

物件模式還可以使用嵌套的 變數模式,讓您可以在與之進行比對的同一行程式碼中從物件中提取(或 解構)屬性值。語法 (:var metadata) 隻是表示「比對並宣告一個與此 getter 名稱相同的變數」。這就是變數 metadata 如何進入最後的 for 迴圈的範圍。非常簡潔!

請注意,for 迴圈現在在每個案例之間是共用的。每個類型的 declaredElement 屬性實際上是另一個類型 InterfaceElement(classElement、enumElement 或 mixinElement)的不同子類型。因此,前 Dart 3 的鏈式 if-else 語句在每個 if 子句中分別迭代 metadata,以確保最終注釋對於 metadata 可能採用的所有可能類型都是類型安全的。

現在,重構後的結構對每個案例使用深度嵌套的物件模式,將 metadata 提升到其超類型 InterfaceElement。這使單個共用 for 迴圈能夠在案例之間迭代 metadata,成為類型安全的。

深度嵌套的物件和變數模式的語法註釋圖

在 Dart 3 的代數資料類型實作中,在物件模式上進行切換很重要,因為它能夠簡潔地測試子類型並解構值。一個很好的副作用是可以透過單行程式碼提供同時的保證。重申一下,此重構中的每個案例模式都同時驗證:

  • 物件是 ClassDeclaration、EnumDeclaration 或 MixinDeclaration 之一。
  • 物件具有 declaredElement 屬性。
  • declaredElement 具有 metadata 屬性。
  • metadata 的類型為 InterfaceElement。
  • 所有正在考慮的物件或屬性都不為 null。

這是 Dart 3 如何徹底實作模式以考慮 OO 語言的許多細微差別,以及如何真正使物件導向代數資料類型成為 Dart 中的實際設計選項的完美範例。

在物件模式上進行類型測試非常適合將行為與類型分開。但它缺少 OO 子類型的其中一個特性,即編譯器可以讓您知道是否宣告了新的子類型,但沒有為其超類型的抽象方法定義行為。當我們不再處理類型宣告上的實例方法時,Dart 的代數資料類型模型如何實作相同的安全保證?答案是 窮舉式檢查

窮舉式檢查

函數式語言對代數資料類型的實作使用可枚舉的總和類型,這意味著編譯器始終知道正在切換的類型的所有可能變體。然後,編譯器可以讓您知道您的 switch 是否缺少案例,因此可能某些值可能會在未被處理的情況下通過該 switch。

這稱為 窮舉式檢查。從技術上講,它始終存在於 Dart 中,用於可枚舉的類型,例如枚舉和布林值。這些類型具有一組不可變化的可能值,如果您要對它們進行切換,編譯器會在您遺漏一個值時告知您。使用預設子句是另一種偽窮舉式檢查。由於預設會匹配所有未明確考慮的案例,它會導致編譯器在不知道是否實際考慮到所有可能的類型的情況下,認為 switch 是窮舉式的。

如前所述,我們想要使用子類型而不是總和類型來進行 Dart 的代數資料類型建模。但由於 Dart 中的類別可以從任何函式庫中進行擴展,因此編譯器不可能窮舉列舉類別的子類型,因為它不知道任何子類別是否在外部函式庫中宣告。

為了解決這個問題並完成 Dart 的代數資料類型實作,我們在 Dart 3 中加入了 封閉類別修飾符。封閉類別不能從除了自身以外的任何函式庫(包含其定義的檔案)進行擴展或實作。這確保編譯器始終知道任何和所有可能的子類型,使其完全可枚舉。

以下是一個作為 3.1 版本發佈的一部分加入到 Dart SDK 中的實際重構範例:封閉 FileSystemEvent,以便可以對其子類型進行窮舉式切換。做好心理準備,重構很困難…

開玩笑的,這一點也不難!不過,需要注意的是,封閉現有的類別階層是一種重大變更。針對舊版本 Dart 的程式碼將無法實作或擴展該類別,因此始終檢查相依性並提醒可能在其他地方子類型化您的類別的任何使用者。

封閉 FileSystemEvent 允許透過 FileSystemEntity.watch 生成的事件(對應於 FileSystemEvent 的子類型)進行窮舉式切換。通常,您會監聽此事件串流並使用 鏈式 if-else 語句來根據發生的事件類型確定操作。

但封閉基類不僅允許您在物件模式上進行切換,例如上一節中的 _checkVisibleOutsideTemplate 範例。它還確保在這樣做的同時,您正在考慮該類型可能產生的所有可能值,而無需使用預設案例:

如果曾經新增了新的子類型,例如 FileSystemSyncEvent,編譯器會知道它,因為它隻能新增到 與 FileSystemEvent 相同的函式庫 中。由於類別階層是封閉的,因此編譯器要求對其實例進行任何切換都必須是窮舉式的,並且會生成 錯誤 來提醒使用者(編寫 switch 的人,而不是函式庫擁有者)未處理的案例:

1
The type 'FileSystemEvent' is not exhaustively matched by the switch cases since it doesn't match 'FileSystemSyncEvent'

將封閉類別和在物件模式上進行切換結合在一起,可以使 Dart 中的完整、物件導向代數資料類型程式設計架構成為可能。

附加功能式特性

上面的窮舉式切換範例包含了比促進代數資料類型更多的 Dart 3 功能特性。

請注意,switch 在 _fileListener 函數的 return 語句的右側 - 那是 Dart 3 中新的 switch 運算式。對運算式和函數的一般強調是函數式語言的關鍵元素。Dart 3 允許生成值的 switch 運算式,並且可以在允許使用運算式的任何地方使用。

那麼,_fileListener 在前面的範例中最終返回什麼?那是個 記錄,這是 Dart 3 的另一個新特性,也與函數式程式設計相關。記錄讓您可以從函數中返回多個異質值,擴展了函數在 Dart 中的用途,並進一步減少了對自訂類別的依賴(這將是除了在過程中不會丟失其類型之外,返回不同類型的多個值的唯一其他方法)。

總結

您可以透過以下方式在 Dart 中建模代數資料類型:

  • 撰寫一個在封閉類別的實例 進行切換,並 其子類型 切換的函數,
  • 並在 switch 案例中定義每個子類型的行為差異。

在物件模式上進行切換允許您以簡潔的方式將所有操作保持在一起,而窮舉式檢查確保編譯器會在您遺漏任何類型的行為定義時提醒您。而且所有這些都建立在 Dart 已經使用的物件導向類別之上。

最棒的是,您不必選擇物件導向或函數式風格;這兩種範式可以融合在一起,您可以使用最適合您正在定義的操作的風格。

您可以透過少量修改使現有的類別階層更具函數式特性,甚至可以在同一個類別階層中混合使用實例方法和代數資料類型。無論將行為與類型緊密結合起來是否有意義,還是將不同類型的行為分組到一個函數中,您都可以使用最合理的風格。

我們希望這個介紹能夠激發您對函數式程式設計以及嘗試使用新的 Dart 3 特性的興趣。誰知道呢,也許我們很快就會看到第一個完全使用函數式風格的 Dart 程式出自你們其中一位!

資源


Dart 3.1 和回顧 Dart 3 中的功能式程式設計風格 最初發佈在 Dart 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

undefined

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

Flutter 2023 年第一季調查:API 重大變更、深度連結等

Flutter 團隊自 2018 年發佈 1.0 版本以來,一直執行季度使用者調查計畫。第 19 次調查 在 2023 年第一季宣布,在為期 14 天的窗口內收集了 13,378 份回覆。調查旨在從 Flutter 開發人員那裡收集及時的回饋,其中包括關於以下主題的問題:

  • Flutter 的感知成長
  • API 重大變更
  • 深度連結

總體而言,93% 的受訪者表示他們對 Flutter 感到滿意(有點滿意 + 非常滿意),其中 55% 表示非常滿意。這個數字自 2022 年 8 月以來一直保持一致。值得注意的是,開發人員對 Flutter 的滿意度仍然很高,因為在這個時間段內,使用者人數增加了 24%。

Flutter 的感知成長

在本節中,我們詢問了受訪者對 Flutter 最近成長的看法。我們相信開發人員對 Flutter 成長的認知會影響他們是否繼續使用它,我們想了解我們的認知是否與使用者的認知一致。

當我們要求受訪者評估最近的成長時,80% 的人表示 Flutter 的普及度成長很快,如下圖所示。這與我們的資料相符,資料顯示,如前所述,過去 3 個季度的月活躍使用者人數增加了 24%。

42% 的人表示 Flutter 的成長非常快,38% 的人表示有點快

根據 80% 在前一個問題中選擇「快」的受訪者(N=2877)的說法,Flutter 團隊的改進是 Flutter 感知快速成長的主要因素(63%)。其他值得注意的因素包括學習資源(57%)、與 Flutter 協同工作的工具和服務(51%)以及全球線上社群(50%)。令人欣慰的是,我們了解到 Flutter 團隊可以改進的領域也促進了感知的成長。我們將繼續推出新的影片,與其他團隊合作改進工具,並支援健康的社群。

對於那些表示該技術成長緩慢的人(4%,或 137 人),最常見的原因是缺乏工作職位(66%)和知名公司採用(55%)。Flutter 的開發人員關係團隊發現,一些招聘經理難以找到合格的候選人,而求職者難以提升技能以滿足招聘公司的要求。為了彌合這一差距,Flutter 團隊計畫致力於更多資源,幫助 Flutter 開發人員在努力找工作時提升技能。我們還將繼續分享 開發者故事,讓您了解其他人是如何將 Flutter 用於他們的生產應用程式。

認為 Flutter 最近成長快速或緩慢的原因

API 重大變更

重大變更是指引入與 API 舊版本不兼容的新行為的變化。重大變更幫助我們保持 API 的乾淨和一致,但開發人員需要更新他們之前編寫的代碼以使用新的行為,無論是重大變更(例如在 Dart 中引入空安全),還是小變更(例如 GestureRecognizer.kind() 的棄用)。

為了減少與此過程相關的痛苦,Flutter 一直在各個地方宣布重大變更(例如 Flutter 網站,以及 Flutter 公告Dart 公告 Google 群組)。在每個穩定版本中,工具都會更新以在適當時顯示警告訊息,並在可能的情況下提供遷移工具(Flutter 修復、dart fix 命令)。Flutter 開發者 2019 年調查詢問了開發者對重大變更的看法,以確保 API 更乾淨。總體而言,開發者喜歡這個想法,但我們被要求牢記以下事項:

  • 慢慢地變更
  • 提供一個良好的遷移工具
  • 提供文件、範例、教學課程、變更日誌
  • 清楚地解釋好處
  • 廣泛地宣布

由於 Flutter 從那時起經歷了顯著的成長(開發者數量和 API 大小都有所增加),我們想再次調查您,並決定是否有必要更新我們的政策。

總體而言,在過去 12 個月內受到重大變更影響的開發人員比例從 4 年前的 30% 上升到 51%。

51% 的開發人員在過去 12 個月內經歷過重大變更

44% 的受訪者受到重大變更影響,他們對必須不斷更新代碼感到沮喪,這也高於 2019 年(32%)。但是,與 2019 年(47%)相比,大多數人同意這些變更使代碼更乾淨(67%)。此外,大多數受訪者同意這些變更很有價值(63%),這比 2019 年的 49% 有所增加。

這表明我們引入的變更的複雜性似乎有所增加,但我們成功地讓這些變更為開發人員帶來了價值。鑑於總體而言 60% 的受訪者認為重大變更的好處多於困擾(而 11% 的人持相反觀點),我們了解到這些變更對 Flutter 開發人員來說在很大程度上是有益的。

對重大變更的看法

我們還詢問了您對重大變更處理方式的滿意度。我們了解到,在我們努力的各個方面,滿意度評級都是一致的。

對 Flutter 團隊處理重大變更的評級

鑑於此,我們決定加倍努力執行我們的現行政策。我們計畫找到更多方法自動化遷移,改善文件的可發現性,並更廣泛地傳達我們所做變更的價值和理由。

我們預計在幾年後再次重新審視這一點。隨著 Flutter 的成熟,我們相信開發人員可能會從重視重大變更中引入的改進轉變為重視永遠不會破壞的 API 的穩定性。我們希望充分利用社群了解重大變更價值的時間,但如果這種態度發生變化,則不要讓 Flutter 開發者群體感到沮喪。

深度連結

深度連結將使用者直接傳送到應用程式內位置,而不是傳送到網站或應用程式商店位置。由於這對開發人員來說是一個有用的功能,我們想測量對這個功能的認知,了解開發人員如何實作這個功能,並評估建立深度連結的難度,以便我們能夠更好地支援這個功能。

首先,我們了解到大約一半的受訪者(55%)知道深度連結。在這些人中,大約 50% 嘗試在他們的 Android 應用程式中實作深度連結,大約 39% 嘗試在他們的 iOS 應用程式中實作深度連結,如下圖所示。在兩個平台上,不到 5% 的人報告他們在實作這個功能時遇到了問題。

在 Android 和 iOS 應用程式中實作深度連結的開發人員百分比

若要為您的應用程式實作深度連結,您需要配置一個 Web 部分和一個應用程式部分。Web 部分將使用者導向應用程式,而應用程式部分則將使用者導向正確的頁面。

在開發人員選擇建立 Web 的技術方面,大約一半的受訪者表示他們使用 Firebase 動態連結。但是,我們最近從 Firebase 團隊了解到,Firebase 動態連結不再推薦用於新專案。動態連結服務將被停用,改為使用原生檔案(Android 的應用程式連結和 iOS 的通用連結)。Firebase 宣布關閉後,您至少有 12 個月的時間進行遷移。如需更多資訊,請查看 動態連結常見問題解答

開發人員用來將使用者從其網站導向 iOS 或 Android 應用程式的工具

對於應用程式設定,使用者採用了不同的方法。命名路由是最簡單的解決方案,是最受歡迎的選擇(31%)。緊隨其後的是 go_router 套件,如果您想要命名路由不支持的功能(例如路由堆疊管理),它是一個更好的解決方案。這些功能在您需要更多地控制深度連結時特別有用。

開發人員用來在應用程式中路由使用者的工具

從調查中,我們了解到我們應該更加努力地解釋這個功能的價值,以便更多開發人員能夠從中受益,並使其更易於實作。我們計畫在接下來的幾個季度推出一個驗證工具,以簡化實作過程。

接下來是什麼?

Flutter 團隊始終渴望聽到您的意見,並將您的回饋納入工程路線圖。本季度的調查特別有價值,因為它讓我們有信心繼續我們的重大變更政策,並幫助我們發現有關深度連結的盲點。我們將繼續聆聽您的意見,並將您的回饋納入我們的 work。

Flutter 團隊致力於為開發人員提供最佳體驗。我們相信,憑藉您的輸入,我們可以建立一個既強大又對開發人員友好的框架。感謝您持續的支持,期待未來收到您的意見。下一次調查見!


Flutter 2023 年第一季調查:API 重大變更、深度連結等 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

Dart DevTools:使用 CPU 分析器分析應用程式效能

無論您是使用 Dart 編寫命令列工具的後端開發人員,還是使用 Flutter 建立應用的 UX 工程師,程式效能對專案的成功至關重要。命令列工具應將延遲降到最低,而應用程式應具有回應性和靈活性,並且沒有掉幀。作為開發人員,我們盡最大努力編寫高效能的程式碼,但有時不清楚為什麼我們的程式碼沒有按照預期執行。

在程式碼庫中追蹤效能問題可能是一項艱巨的任務。有許多方法可以編寫 Flutter 程式碼,使其執行速度比預期慢。有些方法顯而易見,而另一些方法則陰險地微妙。例如,您可能在特定情況下使用了錯誤的 API 或資料結構。

本文將透過一個簡單的案例研究來分析一個緩慢的 Dart 命令列介面 (CLI) 應用程式的效能。您將學習:

  • 一般 CPU 分析器及其重要性。
  • 與 Dart 和 Flutter 捆綁在一起的取樣 CPU 分析器。

有了對 CPU 分析的理解,我們將除錯程式的效能。我們將使用 Dart DevTools CPU 分析器和 dart:developer 的 UserTag 功能來精確找出效率低下的程式碼。我們有很多內容要介紹,所以讓我們開始吧!

注意:Dart DevTools 也稱為 Flutter DevTools,但不要與 Chrome DevTools 混淆。

案例研究:在 Dart 中實作 grep

考慮以下簡單的 Dart CLI 應用程式:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 檔案名稱:grep.dart
import 'dart:io';

/// 在 [檔案] 中尋找並列印 [樣式] 的所有實例。
void grep(File file, String pattern) {
// 開啟檔案並讀取其內容。
final lines = file.readAsLinesSync();

String output = '';
int lineNum = 1;
// 檢查每一行,看看它是否與 `樣式` 匹配。
for (final line in lines) {
final matcher = RegExp(pattern);
if (matcher.hasMatch(line)) {
final foundMessage = '$lineNum:$line';
output += foundMessage;
}
lineNum++;
}
stdout.writeln(output);
}

void main(List<String> arguments) {
if (arguments.length != 2) {
print('使用方法:dart grep.dart <路徑> <樣式>');
exitCode = 64;
return;
}
final path = arguments[0];
final pattern = arguments[1];

final file = File(path);
if (!file.existsSync()) {
stderr.writeln("錯誤:無法開啟檔案 '$path'");
exitCode = 64;
return;
}

grep(file, pattern);
}

grep.dart 程式實作了常見 Unix 工具的版本。它允許使用者在檔案中搜尋符合指定樣式的字元串。例如,給定一個名為 names.txt 的檔案,其中包含:

1
2
3
4
5
6
7
$ cat names.txt
Frank
Bob
Franny
June
Ben
Francis

讓我們找出所有包含字元串 'Fran' 的行:

1
2
3
4
$ dart grep.dart names.txt 'Fran'
1:Frank
3:Franny
6:Francis

太棒了!我們應該從 grep 中得到這個輸出。也就是說,names.txt 是一個 檔案。讓我們嘗試在一個更大的文字文件中找出所有 'Hummingbird' 的實例。如果我們在一個包含 437,000 行、147 MiB 的關於蜂鳥的文字檔上執行相同的程式會怎麼樣?

1
$ dart grep.dart hummingbird_encyclopedia.txt 'Hummingbird'

嗯……即使在 兩分钟 後,它也沒有完成。Unix grep 的效能如何?

1
2
3
4
5
$ grep -n 'Hummingbird' hummingbird_encyclopedia.txt
16:''''Hummingbirds'''' are [[bird]]s native to the…
22:Hummingbirds have the highest…
24:Hummingbirds split from their [[Sister taxon|sister group]]…
// 輸出繼續

Unix 的 grep 搜尋了整個檔案,並在約 45 秒內返回了所有包含蜂鳥的行。顯然,我們的程式碼中有一些奇怪的地方需要調查。但是,我們如何識別效能問題的原因?與 Dart DevTools 捆綁在一起的 CPU 分析器 是一個很好的開始!

什麼是 CPU 分析器?

CPU 分析工具會追蹤程式在執行時花費時間的地方。為了提供最有效率的 CPU 分析體驗,Dart 虛擬機器 (VM)(為 Dart CLI 和 Flutter 應用程式提供動力)使用 取樣 CPU 分析器。當與 Dart DevTools 等工具一起使用時,您可以識別 Dart 程式中的效能瓶頸。

取樣 CPU 分析器採用統計方法來收集應用程式效能資料。它透過定期中斷執行緒並擷取當前呼叫堆疊和其他相關執行狀態的快照來收集樣本。這些樣本可以被處理,以便找出大約有多少時間花費在執行特定函數上,以及函數在不同呼叫堆疊上出現的頻率。

收集樣本的頻率稱為 取樣速率,以每秒樣本數(也稱為赫茲或 Hz)衡量。大多數取樣分析器具有 1000 Hz 或更高的取樣速率。更高的取樣速率會產生更詳細的 CPU 分析,但代價是在目標流程中更高的取樣負擔。在合理的取樣速率下,取樣 CPU 分析器非常有效,並且對被分析應用程式的效能特徵沒有影響。額外的優勢是,與追蹤分析器相比,收集的資料通常在分析方面計算成本更低。

深入探討:取樣分析器是如何運作的?

這部分將詳細介紹 Dart VM 的取樣 CPU 分析器的工作原理。在分析 CPU 分析時,您不需要了解這些細節。如果您不關心取樣 CPU 分析器的詳細資訊,請跳過此部分。

Dart VM 的取樣 CPU 分析器具有三個重要的元件:執行緒中斷器、樣本收集器和樣本處理器。

執行緒中斷器

執行緒中斷器 在一個專用的執行緒上運行,並在 VM 管理的每個執行緒上觸發表 CPU 樣本收集。執行緒中斷器通常處於非活動狀態,只在每個取樣間隔後喚醒一次。在每個取樣間隔之後,中斷器會遍歷執行緒列表,通知每個執行緒暫停並收集樣本。執行緒中斷器在不同平台上的行為略有不同,因為作業系統有特定的細節。

在大多數支援 基於訊號的控制流(Android 和 Linux)的平台上,SIGPROF 訊號會發送到每個執行緒。這會觸發一個 中斷,該中斷會呼叫 CPU 分析器在目標執行緒上註冊的訊號處理器,然後在繼續工作之前收集 CPU 樣本。

在其他不支持訊號(Windows 和 Fuchsia)或在某些情況下使用 SIGPROF 時效能不佳(MacOS 和 iOS)的平台上,執行緒中斷器使用系統呼叫在收集 CPU 樣本後顯式地暫停和恢復每個執行緒。在這種情況下,樣本收集是在執行緒中斷器執行緒上進行,而不是在被取樣的執行緒上進行。

樣本收集

一旦執行緒被中斷,CPU 分析器就會 收集樣本 以擷取執行緒的當前執行狀態。每個 樣本 包含以下類型的中繼資料:

  • 執行緒和隔離區識別碼
  • 執行緒的活動 使用者標記
  • 收集時間戳
  • 被取樣執行緒的當前堆疊追蹤

收集的堆疊追蹤由程式計數器 (PC) 列表組成,這些程式計數器對應於堆疊上找到的每個 Dart 和原生函數的返回地址。這些 PC 是透過一個稱為「遍歷堆疊」的過程收集的。在執行 堆疊遍歷 時,堆疊遍歷器使用頂部框架的框架指標 (FP) 和已知的每個堆疊框架佈局來查找並記錄與函數關聯的 PC 以及前一個堆疊框架的 FP。堆疊遍歷器會重複此過程,使用前一個框架的 FP 作為起點,直到它到達堆疊的末尾,如圖 1 所示。

**圖 1:在 ARM64 系統上收集堆疊追蹤的範例。堆疊遍歷器從 FP(N+1) 開始,從地址 FP(N+1) + 0x10 擷取程式計數器 (PC),並將其加入到堆疊追蹤中作為框架 0。然後,堆疊遍歷器從地址 FP(N+1) + 0x8 查找 FP(N),重複使用 FP(N) 查找框架 1 詳細資訊的相同過程。**

每個收集的樣本都儲存在 VM 的樣本緩衝區中,這是一個 循環緩衝區,可以儲存有限數量的 CPU 樣本。這允許 VM 避免在運行時進行額外的配置,這可能會對效能產生負面影響,或者 如果在訊號處理器中執行,可能會導致不好的事情發生

樣本緩衝區的大小在運行時是固定的,一旦填滿,舊樣本將被分析器覆蓋為新樣本。樣本緩衝區填滿的速度取決於取樣速率和每個樣本的平均堆疊深度。例如,更高的取樣速率和更深的堆疊會導致緩衝區更快地繞回。在 DevTools 中,開發人員可以透過 選擇 低(1000 Hz)、中(4000 Hz)和高(20,000 Hz)取樣速率來控制樣本緩衝區繞回的速度。

樣本處理

當客戶端透過 VM 服務協定 請求 CPU 樣本分析時,CPU 分析器需要在將樣本發送給客戶端之前處理收集的樣本。分析器:

  1. 遍歷樣本緩衝區,使用篩選器只擷取客戶端指定的隔離區和時間範圍內的樣本。
  2. 符號化,或將 PC 對映到函數名稱,樣本集中每個堆疊框架。
  3. 將整個處理後的樣本緩衝區序列化為 JSON。
  4. 將 JSON 傳回給客戶端。

即使在分析器完成處理之後,CPU 樣本回應 仍然是低階的,需要開發人員工具進行額外的處理才能變得有用。例如,Dart DevTools 可以將 CPU 樣本列表轉換為各種結構表示,這些表示允許識別昂貴的函數(自下而上)、昂貴的呼叫路徑(呼叫樹CPU 火焰圖),以及檢查個別方法的呼叫者和被呼叫者統計資料(方法表)。

使用 Dart DevTools 分析 Dart 和 Flutter 應用程式

現在您已經熟悉了取樣 CPU 分析器是什麼以及它們的工作原理,讓我們除錯 grep 實作的效能。讓我們使用 --observe 再次運行程式碼,並打開 Dart DevTools CPU 分析器標籤:

注意:在 DevTools 中測試 Flutter 應用時,您不需要使用 --observe 旗標。

1
2
3
4
$ dart --observe grep.dart hummingbird_encyclopedia.txt 'Hummingbird'
Dart VM 服務正在監聽 http://127.0.0.1:8181/omxEtsCtW9k=/
Dart DevTools 除錯器和分析器位於:http://127.0.0.1:8181/omxEtsCtW9k=/devtools?uri=ws://127.0.0.1:8181/omxEtsCtW9k=/ws
// 在這一點上,終端機掛起,沒有輸出。

哎呀!在 11.6 秒的期間內收集的所有樣本中,隔離區將 90% 以上的 CPU 時間花費在執行 _StringBase.+ 上。這很可能與我們的效能問題有關,但可能不清楚 grep 函數中的哪個程式碼塊是導致延遲的原因。幸運的是,我們可以使用 使用者標記 进一步缩小对 _StringBase.+ 的昂贵呼叫的位置。

使用使用者標記對 CPU 樣本進行分類

當 Dart CPU 分析器中斷執行緒以收集新的樣本時,它會記錄隔離區當前設定的使用者標記。dart:developer 函式庫提供了 UserTag 類別,允許您指定和設定您感興趣的分析程式碼部分的自訂標記。

為了更好地了解我們在 grep 實作中花費時間的地方,我們可以使用使用者標記來 instrumentation 此函數:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
// 檔案名稱:grep.dart
import 'dart:developer';
import 'dart:io';

/// 在 [檔案] 中尋找並列印 [樣式] 的所有實例。
void grep(File file, String pattern) {
final defaultTag = getCurrentTag();
final fileReadTag = UserTag('檔案讀取');
final textMatchTag = UserTag('文字配對');
final printTag = UserTag('列印輸出');

// 將 '檔案讀取' 標記設定為目前的使用者標記。
fileReadTag.makeCurrent();

// 開啟檔案並讀取其內容。
final lines = file.readAsLinesSync();

// 將 '文字配對' 標記設定為目前的使用者標記。
textMatchTag.makeCurrent();
String output = '';
int lineNum = 1;
// 檢查每一行,看看它是否與 `樣式` 匹配。
for (final line in lines) {
final matcher = RegExp(pattern);
if (matcher.hasMatch(line)) {
final foundMessage = '$lineNum:$line';
output += foundMessage;
}
lineNum++;
}

// 將 '列印輸出' 標記設定為目前的使用者標記。
printTag.makeCurrent();
stdout.writeln(output);

// 將使用者標記重置為呼叫 grep 時設定的標記。
defaultTag.makeCurrent();
}

現在,讓我們重新運行程式碼並打開 CPU 分析器。若要查看已分類的分析,請從下拉選單中選擇 分組依據:使用者標記 選項:

當我們展開 文字配對 標記時,我們確認 _StringBase.+ 方法在我們的文字配對迴圈中被呼叫:

有了這些資訊,我們應該能夠仔細查看我們的程式碼並找出問題所在:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 將 ‘文字配對’ 標記設定為目前的使用者標記。
textMatchTag.makeCurrent();
String output = '';
int lineNum = 1;
// 檢查每一行,看看它是否與 `樣式` 匹配。
for (final line in lines) {
final matcher = RegExp(pattern);
if (matcher.hasMatch(line)) {
final foundMessage = '$lineNum:$line';
// 哈哈!這就是我們呼叫 `_StringBase.+` 的地方!
output += foundMessage;
}
lineNum++;
}

找到了!我們犯了經典錯誤,多次將內容附加到 String,而不是使用 StringBuffer。將內容附加到 String 會建立一個新的字元串來儲存 _StringBase.+ 方法的結果。因此,每次我們找到匹配項時,我們都會將 output + foundMessage 複製到一個新的字元串中。

隨著 output 變長,將資料附加到它會變得更昂貴,需要 O(m*n) 來執行複製,其中 m 是匹配項的平均字元數,n 是最終字元串中的總字元數。如果我們使用 StringBuffer,我們不會在每次附加時複製 output,而是在函數結束時以單一的 O(n) 操作將匹配連接起來。

現在我們的應用程式使用 StringBuffer.writeln 而不是將內容附加到 String,讓我們看看我們的函數:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 檔案名稱:grep.dart
import 'dart:developer';
import 'dart:io';

/// 在 [檔案] 中尋找並列印 [樣式] 的所有實例。
void grep(File file, String pattern) {
final defaultTag = getCurrentTag();
final fileReadTag = UserTag('檔案讀取');
final textMatchTag = UserTag('文字配對');
final printTag = UserTag('列印輸出');

// 將 '檔案讀取' 標記設定為目前的使用者標記。
fileReadTag.makeCurrent();

// 開啟檔案並讀取其內容。
final lines = file.readAsLinesSync();

// 將 '文字配對' 標記設定為目前的使用者標記。
textMatchTag.makeCurrent();
final output = StringBuffer();
int lineNum = 1;
// 檢查每一行,看看它是否與 `樣式` 匹配。
for (final line in lines) {
final matcher = RegExp(pattern);
if (matcher.hasMatch(line)) {
final foundMessage = '$lineNum:$line';
// 將匹配項加入到緩衝區中,而不建立副本。
output.writeln(foundMessage);
}
lineNum++;
}

// 將 '列印輸出' 標記設定為目前的使用者標記。
printTag.makeCurrent();
// `output.toString()` 將緩衝區中的每個項目連接成一個新的 String,只執行一次大小為 `output.length` 的配置。
stdout.writeln(output);

// 將使用者標記重置為呼叫 grep 時設定的標記。
defaultTag.makeCurrent();
}

讓我們再次運行測試,看看是否有任何改善:

1
2
3
4
5
$ dart grep.dart hummingbird_encyclopedia.txt 'Hummingbird'
16:''''Hummingbirds'''' are [[bird]]s native to the…
22:Hummingbirds have the highest…
24:Hummingbirds split from their [[Sister taxon|sister group]]…
// 輸出繼續

使用 StringBuffer,我們可以在約 45 秒 內找出所有 'Hummingbird' 的實例。這好多了,幾乎與 Unix grep 實作相同!讓我們再看一下 CPU 分析器,看看是否可以進一步提高效能:

快速瀏覽分析告訴我們,我們大部分時間都花費在列印匹配項上,而實際的匹配只花費了約 200 毫秒。我們應該關注 '列印輸出' 標記下的程式碼:

1
2
3
4
// 將 ‘列印輸出’ 標記設定為目前的使用者標記。
printTag.makeCurrent();
// `output.toString()` 將緩衝區中的每個項目連接成一個新的 String,只執行一次大小為 `output.length` 的配置。
stdout.writeln(output);

似乎我們沒有什麼可以做的,因為我們只對核心 dart:io 函式庫的成員 stdout.writeln 進行了一次呼叫。查看 分析器中的 CPU 火焰圖,我們看到這段程式碼在 SDK 中,我們無法在程式碼中獲得更多效能增益。

就這樣!

結語

從這篇文章開始,我們已經取得了很大的進展。我們:

  • 使用 Dart 編寫了一個簡單的 grep 工具。
  • 確定我們的程式效能很差。
  • 了解了 CPU 分析器,並探索了 Dart VM 的取樣 CPU 分析器。
  • 使用 Dart DevTools 的 CPU 分析器 找出並修復了程式的效能問題。

深入了解效能工具是一項重要的技能,正如我們在本文中所展示的,它可以幫助您找出程式碼中可能存在著微妙的效能問題。CPU 分析器只是 Dart DevTools 附帶的許多工具之一,可以幫助您更好地了解 Dart CLI 和 Flutter 應用的行為和效能。

在未來的文章中,我們將探索使用 Dart DevTools 除錯和優化應用的其他方法,包括:

  • 使用 記憶體 畫面分析記憶體使用情況。
  • 使用 效能 畫面檢查執行時間線。
  • 使用 網路 畫面檢查 HTTP 流量。

在那之前,祝您編程愉快!

如果您願意,在 GitHub 上關注我 以了解我對 Flutter 和 Dart 虛擬機器的最新工作,以及我的其他個人專案。


Dart DevTools:使用 CPU 分析器分析應用程式效能 最初發佈在 Dart 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

undefined

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

結合 AI 生成的文字和圖片,為這款使用 Flutter、Firebase 和 Google Cloud 建立的紙牌遊戲增添趣味

I/O FLIP 是一款由 Google 支援並使用 Flutter 建立的 AI 設計紙牌遊戲。它結合了許多 Google 產品和技術,包括 FlutterFirebase、生成式 AI 工具、Muse 上的 Dreambooth、PaLM APIMakerSuite。玩家可以進行多場比賽,並將遊戲中的牌分享到社群媒體。

要玩遊戲,請訪問 http://flip.withgoogle.com。要開始遊戲並生成牌組,請回答幾個提示。要組牌並加入比賽,請選擇三張牌。您將使用這些牌與您的對手進行三輪比賽,每輪比賽以最高牌獲勝。當比賽結束時,樂趣不必結束。繼續進行比賽以提高您的連勝紀錄,並試圖登上排行榜!

讓我們深入探討我們如何使用 Flutter 和 Firebase 來建立 I/O FLIP 的技術細節。

使用 Flutter 建立的紙牌遊戲

I/O FLIP 的使用者介面、動畫、全息影像效果和後端,都使用 Flutter 和 Dart 建立。

首先,我們利用了 Flutter 休閒遊戲工具包。我們使用開箱即用的音訊功能、遊戲生命週期和 go_router 的應用程式導航作為基礎。從那裡,我們建立了遊戲邏輯和 UI。I/O FLIP 是一個 響應式 Web 應用程式。它根據玩家的螢幕調整其 UI。該應用程式還根據玩家使用的設備處理輸入。當在行動裝置或平板電腦上訪問時,它接收觸控輸入,而在桌上型電腦上接收滑鼠輸入。

撲克牌是 I/O FLIP 的關鍵元素。成千上萬的牌可能會出現在玩家的牌組中。每張牌都顯示一個 Google 吉祥物、元素和力量值。當元素彼此對抗時,這些元素就會發揮作用。例如,當有人打出一張火牌,而他們的對手打出一張水牌時,水牌會受到 10 分的懲罰。我們還使用紀錄,Dart 3 的新功能,根據牌的元素來渲染一個框架。


(String, SvgPicture) _mapSuitNameToAssets() {
switch (suitName) { case 'fire':
if (isRare) { return ( Assets.images.cardFrames.holos.cardFire.keyName, Assets.images.suits.card.fire.svg(), ); } return ( Assets.images.cardFrames.cardFire.keyName, Assets.images.suits.card.fire.svg(), ); …

使用 AI 生成的圖片和描述建立的牌

I/O FLIP 例子牌,顯示 Dash、Sparky、Android 和 Dino,背景中是各種背景和道具。
I/O FLIP 例子牌

I/O FLIP 中的每張牌都是獨特的,因為它包含 AI 生成的圖片和描述。在遊戲開始時,玩家會回答兩個提示。這些提示有助於填充一副包含 12 張牌的牌組,這些牌組的圖片和描述是 AI 模型預先生成的。

Google 團隊使用了兩種技術來預先生成圖片:Muse,一個 Transformer 文字轉圖片模型,以及 Dreambooth。Dreambooth 使得能夠在不同的場景、姿勢、視角和光照條件下合成一個主體。每張牌都包含四個 Google 吉祥物之一:Dash、Sparky、Android 或 Dino,以及一個地點。吉祥物還有一個物品,用來指定他們的隊伍。「隊伍類型」提示在遊戲開始時就會被覆蓋,用於建立這個物品。例如,選擇「巫師」可能會導致一個角色戴著巫師帽、魔杖,或其他巫師般的事物!

Google 團隊使用 PaLM API 來預先生成牌的描述。PaLM API 訪問 Google 的大型語言模型。遊戲開始時的提示包含團隊類型和他們團隊的力量類型。假設您選擇了「巫師」團隊和「磁力」力量。當您的牌生成時,一張牌的描述包含與生成的圖片相關的上下文,包括角色的特殊力量。例如,「Dash 巫師和他的寵物龍住在一座城堡裡。他喜歡施法,讓大家開心。」

Flutter 透過 GameCard Widget 組合牌。這個 Widget 接收牌的資料:名稱、描述、圖片和力量。一旦它建立了牌,它就會應用邊框以描繪牌的套裝元素。如果一張牌是特殊牌,Flutter 會應用箔紋理效果。

若要進一步了解遊戲的生成式 AI 方面是如何建立的,請查看 這篇 Google 開發者部落格文章

著色器為特殊牌添加箔效果

兩張牌在比賽中,一張牌顯示皇家 Sparky 戴著皇冠,點數為 100,並帶有彩虹箔效果,這是通過使用片段著色器實現的
I/O FLIP 使用片段著色器來渲染牌上的全息效果

Flutter 支援 片段著色器。為了生成這些逐像素的視覺效果,Flutter 在裝置的 GPU 上運行 OpenGL 著色語言 (GLSL)。集換式卡牌遊戲的收藏家可能會記得打開一包卡牌時,找到一張有閃亮的全息箔的特別版卡牌的感覺 I/O FLIP 也包括特殊的箔牌。它們的點數為 100 分。普通牌的點數範圍為 10 到 99 分。我們使用自訂著色器來渲染箔效果。

我们在 foil.frag 文件中实现了箔着色器。该效果使用以下常量:

  • STRENGTH. 这会将原始像素颜色与箔效果的颜色混合。它从 0.0(无效果)到 1.0(全效果)。
  • SATURATION. 这会设置颜色的强度。它从 0.0(灰度或无颜色)到 1.0(全颜色或无黑色)。
  • LIGHTNESS. 这从 0.0(全黑)到 1.0(全白)。

该着色器还通过 uniform 接收输入,在本例中为 resolution 和 offset。称为 tSource 的 uniform sampler2d 代表应用了着色器的卡牌图像。最终结果是带有箔效果的卡牌。

vec4 rainbowEffect(vec2 uv) {
    vec4 srcColor = texture(tSource, uv);
    float hue = uv.x / (1.75 + abs(offset.x)) + offset.x / 3.0;
    float lightness = LIGHTNESS + 0.25 * (0.5 + offset.y * (0.5 - uv.y));
    hue = fract(hue);

    float c = (1.0 - abs(2.0 * lightness - 1.0)) * SATURATION;
    float x = c * (1.0 - abs(mod(hue / (1.0 / 6.0), 2.0) - 1.0));
    float m = LIGHTNESS - c / 2.0;

    vec3 rainbowPrime;

    if (hue < 1.0 / 6.0) {
        rainbowPrime = vec3(c, x, 0.0);
    } else if (hue < 1.0 / 3.0) {
        rainbowPrime = vec3(x, c, 0.0);
    } else if (hue < 0.5) {
        rainbowPrime = vec3(0.0, c, x);
    } else if (hue < 2.0 / 3.0) {
        rainbowPrime = vec3(0.0, x, c);
    } else if (hue < 5.0 / 6.0) {
        rainbowPrime = vec3(x, 0.0, c);
    } else {
        rainbowPrime = vec3(c, 0.0, x);
    }

    vec3 rainbow = rainbowPrime + m;
    return mix(srcColor, vec4(rainbow, srcColor.a), STRENGTH);
}

Firebase 啟用遊戲託管和分享到社群媒體

所有遊戲玩法通訊都是透過 Firebase 的 Cloud Firestore 實時進行的。Firebase 儲存空間託管生成玩家牌組的牌的資產。我們還使用 Cloud Firestore 來追蹤排行榜上的「最高連勝紀錄」。當排行榜添加新的領先者時,firedart 套件會將其添加到 Firestore 中。

Dart Frog 啟用後端和前端之間的程式碼共享

I/O FLIP 需要一個後端來防止作弊。這個伺服器授權的遊戲邏輯可以防止惡意的客戶端發送偽造的請求。Dart Frog 將遊戲邏輯(例如每輪的獲勝者)保留在後端。它還將此程式碼在 Flutter 前端和 Firestore 後端之間共享。共享程式碼有一些好處。它允許我們共享邏輯。例如,如果一個玩家贏得一輪,遊戲可以在不每次都需要查詢 Firebase 的情況下顯示獲勝動畫。共享程式碼還加快了開發速度,因為團隊可以使用同一種語言(Dart)撰寫後端和前端程式碼。我們將 I/O FLIP Dart Frog 伺服器部署到 Cloud Run。這表示伺服器程式碼在 Google Cloud 中運行,並且可以自動縮放,因此應用程式可以同時處理許多玩家。

FutureOr<Response> onRequest(RequestContext context) async {
  if (context.request.method == HttpMethod.post) {
    final cardsRepository = context.read<CardsRepository>();
    final promptRepository = context.read<PromptRepository>();

    final body = await context.request.json() as Map<String, dynamic>;
    final prompt = Prompt.fromJson(body);

    if (!await promptRepository.isValidPrompt(prompt)) {
      return Response(statusCode: HttpStatus.badRequest);
    }

    final characterClass = prompt.characterClass;
    if (characterClass == null) {
      return Response(statusCode: HttpStatus.badRequest);
    }

    final characterPower = prompt.power;
    if (characterPower == null) {
      return Response(statusCode: HttpStatus.badRequest);
    }

    final cards = await cardsRepository.generateCards(
      characterClass: characterClass,
      characterPower: characterPower,
    );
    return Response.json(
      body: {'cards': cards.map((e) => e.toJson()).toList()},
    );
  }
  return Response(statusCode: HttpStatus.methodNotAllowed);
}

Dart Frog 還促進了在社群媒體上的分享。當選擇牌時,玩家可以將單張牌分享到 Twitter 或 Facebook。當您將分數提交到排行榜時,您可以將手牌分享到 Twitter 或 Facebook。一旦使用者點擊分享,Dart Frog 會生成一個預先設定好的帖子。此帖子包含文字和指向具有您對應的手牌或牌的網頁的連結,以及一個按鈕,供訪客自己玩 I/O FLIP!

接下來是什麼

I/O FLIP 展示了如何在一個全球玩家都可以玩的有趣遊戲中組合 Flutter 和 Firebase,以及 Google 的生成式 AI 工具和技術。

玩一局遊戲,向我們展示你的手牌,或者深入研究 開源程式碼


製作方法:I/O FLIP 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。