0%

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

慶祝 Flutter 的「生產時代」

就在六年前,我們推出了 Flutter 1.0。今天,在 #FlutterInProduction 上,我們正在慶祝我們取得的成就——從社群中 數千名貢獻者 的巨大支持,到 Flutter 作為用於建構跨平台應用程式體驗的生產級應用程式框架的 廣泛採用

如果您還沒有體驗過 Flutter,我們邀請您試用一下!正如我們今天分享的那樣,您將加入一個龐大的群體:Flutter 在全球擁有 超過 100 萬月活躍開發人員,並且 為近 30% 的所有新 iOS 應用程式提供支援。超過 9 萬名開發人員積極參與了 60 多個國家的 Flutter 聚會。如果您希望在設計或建構新的成功的 Flutter 應用程式方面獲得意見,我們有一個龐大且不斷增長的 Flutter 顧問 列表,隨時準備為您提供幫助。

“Apptopia 追蹤 Apple AppStore 和 Google Play 商店中的數百萬個應用程式,並分析和檢測用於建立應用程式的開發人員 SDK。Flutter 是我們追蹤的最受歡迎的 SDK 之一:在 Apple AppStore 中,它的使用率從 2021 年所有追蹤的免費應用程式的約 10% 穩步增長到 2024 年所有追蹤的免費應用程式的近 30%!”

Apptopia Inc.

十年的創新,邁向生產時代

這是一段不可思議的旅程,始於 2014 年(也就是我們現在所說的實驗時代),當時它是 Google 的一個代號為「Sky」的實驗。

在 Flutter 出現之前,妥協是不可避免的。許多開發人員已經開始懷疑,任何框架是否都能夠真正跨多個平台提供優質的體驗。

隨著 2018 年 Flutter 1.0 的推出,我們有了一個明確的使命來解決這個技術困境:我們的目標是為開發人員提供終極應用程式框架,以便在所有平台上製作美觀、高效能的使用者介面。此外,還要讓開發人員能夠在客戶關心的所有平台上以高品質的應用程式接觸所有客戶,但 成本更低時間更短

即使我們增加了對行動、網頁和桌面六大主要平台的支援,並且透過豐田將 Flutter 用於資訊娛樂系統等工作 繼續拓展,我們的重點在 Flutter 的增長期一直保持不變。

我們現在正處於「生產時代」,我們正在用 #FlutterInProduction 慶祝這一點!此活動重點介紹了在實際應用程式中使用 Flutter 的開發人員所取得的成就。

與社群合作建構

沒有我們超過 1,400 名貢獻者、超過 10,000 名套件發佈者(編寫了超過 50,000 個套件)以及充滿熱情的 Flutter 內容創作者和愛好者的驚人社群,這一切都不可能實現。

我們共同建構了一個 GitHub 貢獻排名前五的開源專案

令人驚嘆的使用者體驗

這一切都始於對實現令人驚嘆的使用者體驗的關注。擺脫了典型的平台限制,Flutter 支援廣泛的設計語言——SDK 附帶了對 Material Design 和我們受 Apple 啟發的 Cupertino Widget 的支援。生態系統還提供了廣泛的設計函式庫選擇,例如 Windows 啟發的 fluent_ui、macOS 啟發的 macos_ui 和 Ubuntu 啟發的 yaru Widget。

斯堪地那維亞航空公司設計獎

使用 Flutter,您擁有實現設計團隊預想的 任何設計 的靈活性和能力。斯堪地那維亞航空公司就是一個很好的例子,他們在使用 Flutter 建立了新的行動應用程式後,獲得了許多著名的設計獎項,例如紅點設計獎、威比人民之聲獎和 iF 設計金獎。SAS 的執行副總裁兼首席資訊長 Charlotte Svensson 解釋說

“我對這個獎項感到非常自豪,它不僅僅是一個行業獎項,而是一種全球認可。這證明了當我們超越自我,專注於改善客戶體驗,以及當我們與客戶互動和共同發展時,我們能夠做到什麼。SAS 一直處於航空業創新的前沿,這個獎項也驗證了我們致力於為客戶提供卓越的數位解決方案的承諾。”

Scandinavian Airlines design awards

出色的效能和可靠性

效能和可靠性對於積極的使用者體驗和品牌認知至關重要。速度緩慢或容易崩潰的應用程式不僅會在短期內讓使用者感到沮喪,而且還會透過負面評論和口碑在長期內損害您的品牌聲譽。

Flutter 從一開始就優先考慮效能和可靠性。透過選擇 Dart 程式語言,我們確保透過提前編譯為原生機器碼或 WebAssembly 來實現快速啟動時間。Dart 豐富的空安全類型系統有助於在開發過程中捕獲錯誤,從而進一步提高可靠性。此外,Flutter 的自訂 Impeller 渲染引擎專為跨平台 UI 設計,可提供流暢的動畫,並讓我們完全控制從 UI 原始碼到 GPU 的整個渲染堆疊。

環球影城的效能和可靠性

例如,環球目的地與體驗最近報告說,透過採用 Flutter,他們不僅減小了應用程式的大小(這對於網路連線不可靠的使用者來說是一個很大的好處),而且還大幅減少了應用程式崩潰,幾乎為零,從而降低了他們的總擁有成本。

LG 電子的效能

由於擔心傳統原生應用程式的高開發成本,LG 電子傳統上一直依賴 Web 應用程式來開發其 webOS 驅動的智慧電視。但是,他們發現 Web 應用程式的啟動速度比原生應用程式慢,而且佔用更多記憶體。透過 Flutter,LG 電子擁有一個結合了快速開發速度和出色效能的解決方案。因此,他們計劃從 2025 年開始在全球範圍內將 Flutter 用於 webOS 電視中的關鍵應用程式。

一流的開發人員體驗和蓬勃發展的生態系統

Flutter 的成功深深植根於其對開發人員體驗的關注。我們使用 Stateful Hot Reload 開創了即時開發人員工作流程,並在我們的增長期增加了 Flutter DevTools,以顯著加快診斷和除錯工作流程。

Flutter 的社群提供了一個由超過 10,000 名發佈者發佈的超過 50,000 個套件組成的蓬勃發展的開放生態系統,以及強大的第三方服務和技術。此外,如果您希望在設計或建構新的成功的 Flutter 應用程式方面獲得意見,我們有一個龐大的 Flutter 顧問 列表,隨時準備為您提供幫助。

MGM 和開發人員生產力

應用程式代理商 Superformula 自 2020 年 8 月以來一直在使用 Flutter 進行建構。他們發現 Flutter 易於學習且文件齊全,使他們能夠讓新團隊成員快速上手並有效地做出貢獻。

Superformula 還使用 Flutter 為 MGM Resorts 的 400 多家餐廳 重振數位用餐體驗。新的基於 Flutter 的 MGM Rewards 應用程式僅在 4 個月內就重建完成,程式碼總量減少了一半,交付速度提高了 4 倍。Superformula 生產力的一個核心推動因素是能夠在行動設備、基於平板電腦的資訊站和基於 Web 的工具之間共用程式碼。

GEICO 和行動開發的民主化

今年早些時候,美國汽車保險公司 GEICO 發表了一篇部落格文章,詳細介紹了他們使用 Flutter 的經驗以及 Flutter 如何提高他們的生產力。他們能夠改變其組織結構,以便 UX 團隊現在 跨所有管道 擁有整個體驗,減少了相同功能的不同實作之間的差異。他們還將 Flutter 的開發體驗描述為 “成熟的,具有豐富的工具和對軟體開發生命週期 (SDLC) 的支援”

GEICO user interface elements shared across web, iOS, and Android.

未來搶先看

正如我們所說,Flutter 團隊在未來幾年將承擔的一些最重要的工作是維持向全球數億使用者提供基於 Flutter 的體驗的企業所要求的支援的品質和深度。

為了滿足這些不斷發展的需求,我們正專注於幾個關鍵領域:

  • 更深層次的 iOS 保真度: 我們將繼續透過擴展 Cupertino Widget 來提供對 Apple 設計語言的更高保真度。我們正在擁抱現代 Apple 生態系統標準,例如 Swift Package Manager。
  • 無縫平台整合: 我們正在研究一種新的互操作方法——直接原生互操作——我們相信這將簡化對 C、Java、Kotlin、ObjectiveC 或 Swift 中可用的平台特定 API 的存取。
  • 開發人員生產力增強: 我們正在繼續發展我們的開發人員工具,透過探索潛在的新的 UI 設計功能,這些功能可能會提供實時 Widget 預覽器以及實時視覺屬性編輯。
  • 簡潔易讀的程式碼: 為了提高開發人員的生產力,我們正在探索如何使 Flutter 程式碼更快地編寫和更容易閱讀。這包括潛在的新的語言功能,例如「主要建構函式」和「列舉簡寫」,並瀏覽 Flutter 框架以識別可能提供更簡潔的編寫常用程式碼方式的新 API。

正如 Rivers 如此準確地說的那樣,事情確實 “變得更流暢、更專業了一些”,我們的目標是讓事情在未來繼續保持這種感覺。

準備好在生產中使用 #FlutterInProduction 了嗎?

如果您最近沒有嘗試過 Flutter,那麼現在就是最佳時機!

想先了解別人的經驗嗎?

我們致力於透過始終如一地關注品質和效能來贏得您的信任。我們繼續根據使用者的需求來推動我們的路線圖。在過去 6 年的增長期中,這也發生了變化,從實驗時代的個人和業餘愛好者,到如今利用 Flutter 處理業務關鍵功能的大型企業。

與往常一樣,我們迫不及待地想看看您接下來會建構什麼。下次再見!


慶祝 Flutter 的「生產時代」 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

宣佈 Flutter AI 工具包

Flutter AI 開發者們,歡呼吧!將 AI 支援的聊天體驗新增到您的應用程式中變得更加容易。隆重推出 Flutter AI 工具包,這是一組隨時可用的 AI 聊天 Widget,旨在無縫整合到您的 Flutter 專案中。無需再從頭開始構建複雜的聊天介面 - Flutter AI 工具包提供了快速輕鬆地將精密的 AI 聊天視窗新增到您的應用程式所需的一切。準備好提升跨多個平台的客戶參與度和滿意度了嗎?您已經準備好了!

在 Project IDX 中試用 Flutter AI 工具包!

多輪聊天

透過多輪聊天,您可以在同一個會話中的多個互動之間維持上下文。

想像一下您正在與朋友交談。他們問您是否有寵物,您告訴他們您有一個名叫 Rocky 的寵物石頭。過了一會兒,在與您聊了其他事情之後,您的朋友問:「您養 Rocky 多久了?」朋友記得您之前談話中提到的寵物石頭,即使您沒有再次提及它。

這基本上就是多輪聊天的作用!它可以幫助大型語言模型 (LLM) 記住正在談論的內容。

Flutter AI 工具包中的多輪聊天

富文本顯示

LLM 生成的回應支援富文本格式。LLM 不僅可以生成純文字,還可以將文字格式化,使其更有趣。例如,回應可以包含粗體文字、圖片、項目符號等等。

Flutter AI 工具包中的富文本格式支援

語音輸入

語音輸入可用於將訊息轉換為文字。此免持選項非常適合那些不喜歡打字或想要更快地撰寫訊息的人。

Flutter AI 工具包中的語音輸入支援

多媒體附件

圖片、影片、音訊、PDF 和其他檔案可以單獨或一起新增到對話中。

Flutter AI 工具包中的多媒體附件支援

自訂回應 Widget

為回應設計專門的 UI 元件。例如,如果有人要求食譜,您可以顯示一個互動式食譜卡,並提供將食譜直接儲存到資料庫的選項。

Flutter AI 工具包中的自訂 AI 回應 Widget 支援

自訂樣式

設定聊天外觀以符合您的應用程式。變更聊天氣泡的顏色、背景、UI 字體,甚至新增 UI 圖片或動畫以反映您的品牌識別。

Flutter AI 工具包中的自訂樣式支援

跨平台支援

Flutter 支援許多平台,Flutter AI 工具包也支援。想要在 iOS、Android、Web 和 macOS 上部署使用 Flutter 製作的 AI 聊天應用程式嗎?沒問題!

可插拔的 LLM

無論您是想使用 Google 的 Gemini 或 Vertex AI,還是其他 LLM,Flutter AI 工具包都可以輕鬆地將 AI 新增到您的應用程式中。GeminiProvider 和 VertexProvider 可用於開箱即用的整合,分別使用 Google AI Dart SDKFirebase 中的 Vertex AI SDK。我們強烈建議您將 Firebase 中的 Vertex AI SDK 用於原型設計以外的生產使用案例。此外,您可以利用 Flutter AI 工具包的 LLM 提供者介面,使用您自己的自訂程式碼插入您選擇的 LLM。

具有多個對話的獨立聊天應用程式

構建一個功能齊全的聊天應用程式,可以儲存和管理多個對話。為了幫助您入門,Flutter AI 工具包附帶了一個範例專案,該專案利用了 Cloud Firestore 資料庫和 Firebase 中的 Vertex AI

Flutter AI 聊天應用程式

立即試用 Flutter AI 工具包

要親自試用 Flutter AI 工具包,請查看 pub.dev 上的 Flutter AI 工具包 以及以下資源:


宣佈 Flutter AI 工具包 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

Flutter 的影片和網頁廣告支援

共同作者:Zoey Fan、Harold Benson、Alak Jhunjhunwala

將您的 Flutter 應用程式變現變得更容易,也更有價值。今天,我們很高興地宣布一套新的工具和功能更新,為開發人員提供一系列選項來將您的應用程式變現並發展您的業務。

Flutter 開發人員現在可以使用影片廣告

顯示範例影片廣告的動畫 gif

讓我們先將您的影片內容轉變為創造收入的強大引擎!

我們要宣布推出 Flutter 的互動式媒體廣告 (IMA) Plugin。您現在可以將高品質的影片廣告無縫整合到 iOS 和 Android 上的 Flutter 應用程式中。這意味著您現在可以在影片內容播放之前(片頭廣告)、期間(中間廣告)或之後(片尾廣告)輕鬆地透過影片廣告將影片內容變現。就像 Google 行動廣告 Plugin 簡化了 Flutter 應用程式中行動廣告的顯示一樣,IMA Plugin 讓您輕鬆地整合影片廣告。我們已經完成了繁重的工作,因此您可以專注於最重要的事情:創造令人驚嘆的應用程式體驗。

輕鬆整合,最大限度地發揮影響力

  • 客戶端廣告插入: 廣告在單獨的影片播放器中播放,確保為您的使用者提供流暢的觀看體驗,同時讓您擁有更多控制權。
  • 無縫廣告處理: Plugin 請求影片廣告並處理播放。它還確保渲染跳過按鈕等 UI 元素,並觸發廣告測量訊號。
  • 符合產業標準: IMA 利用產業標準的影片廣告投放範本 (VAST) 規範,確保與廣告伺服器的廣泛相容性。
  • 存取優質廣告需求: IMA 確保您可以透過 Google 的高品質和優質影片廣告需求來將您的影片內容變現。

立即開始!

您準備好使用影片廣告為您的 Flutter 應用程式增壓了嗎?前往 pub.dev/packages/interactive_media_ads 以探索 IMA Plugin 並存取詳細的文件。您將找到開始使用所需的一切,包括程式碼範例和逐步指南。我們很期待看到您如何使用 IMA Plugin 將您的 Flutter 影片內容變現,並創造更具吸引力的應用程式體驗!

Beta 版:使用 AdSense 廣告將您的 Flutter 網頁遊戲變現

顯示遊戲中範例 AdSense 廣告的動畫 gif

許多 Flutter 開發人員希望像他們的行動應用程式一樣,將他們的 Flutter 網頁專案變現。我們一直在幕後忙碌地工作,並且有一些令人興奮的進展可以分享!

今天,我們很興奮地宣布 H5 遊戲 Plugin 的 AdSense beta 計劃!透過此 Plugin,您可以輕鬆地將 AdSense H5 遊戲廣告 加入到您在 Flutter 中建立的網頁遊戲 (H5) 中,開闢新的收入來源。

beta 計劃提供的內容:

  • 成為首批整合 AdSense 的人之一:搶先體驗 Flutter AdSense Plugin,並開始試驗不同的廣告格式。
  • 塑造 Plugin 的未來:提供寶貴的回饋,並幫助我們根據 Flutter 網頁開發人員的特定需求定制 Plugin。

此計劃是邁向 Flutter 網頁完整廣告變現解決方案的重要一步。目前,此 Beta 計劃的重點是為 Flutter 網頁遊戲 啟用 AdSense。我們正在與 AdSense 團隊合作,為未來的 Flutter 網頁應用程式開發解決方案。

如果您是 Flutter 網頁 遊戲 開發人員,並且有興趣參與,請填寫 H5 AdSense 申請表。一旦您獲准加入 beta 計劃,請使用 pub.dev/packages/google_adsense 開始使用!

透過深度連結提高轉換率和更好的使用者體驗

加入廣告是成為成功企業的方法之一。有時您的企業需要在應用程式中銷售商品和服務。您可能已經花費了大量的時間和資源來建立簡訊/電子郵件行銷和廣告活動,並希望這些活動能夠帶來有效的轉換。但是,如果您能透過將客戶從行銷活動直接引導到您的應用程式以及他們感興趣的特定項目,從而將您的活動提升到新的水平呢?

透過 Flutter 中的 深度連結,您可以做到這一點。當有人點擊您活動中的連結時,他們將被直接帶到您的應用程式以及您正在推廣的產品或服務。與連結到您的網站相比,這種無縫的轉換將 轉換的機率提高了 2.3 倍

以下是深度連結對廣告商來說改變遊戲規則的原因:

  • 提高轉換率: 透過將客戶直接帶到您的應用程式,您可以消除干擾,並讓他們更容易完成購買。
  • 個人化體驗: 深度連結允許您為每個客戶創造個人化體驗。當他們點擊連結時,他們將被帶到他們感興趣的特定產品或服務。
  • 追蹤活動績效: 透過深度連結,您可以追蹤活動的有效性,並查看有多少人點擊連結到您的應用程式。這些資料可以幫助您優化活動以獲得更好的結果。

雖然深度連結提供了所有這些優點,但設定起來可能相當麻煩。因此,我們也讓深度連結比以往任何時候都更加穩健和可靠。透過 Flutter DevTools 中 iOS 和 Android 的完整 網頁和應用程式檢查驗證支援,您可以確保您的深度連結已正確且安全地設定。您的使用者將享受網頁和應用程式內容之間的無縫轉換,增加參與度和滿意度!首先,前往 Flutter DevTools,並打開深度連結驗證標籤。

顯示 Flutter DevTools 中深度連結驗證的動畫 gif

我們致力於為您(Flutter 開發人員)提供您成功所需的工具和資源。這些新功能證明了我們的承諾。所以,繼續,將您的 Flutter 應用程式提升到新的高度,並釋放其全部潛力!

探索更多:


Flutter 中的影片和網頁廣告支援 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

宣佈 Dart 3.6

歡迎來到 Dart 3.6!今天,我們發佈了今年的最後一個版本,與 Flutter 3.27 一起推出。我們很高興在語言中分享一個新功能:數字分隔符。我們在 pub 生態系統中也有一些新功能:pub 下載次數 以及備受期待的 pub 工作區

Dart 3.6

Pub 工作區

Pub 現在支援在 monorepo 或工作區中的套件之間共用解析。工作區是一組緊密相關的套件,它們一起開發、解析和發佈。

pub 工作區功能可確保 monorepo 中的套件共用一組一致的相依性。這會強制您 在出現時 解決分組套件之間的相依性衝突,而不是在開始使用套件時才面對混亂。

Flutter 分析器會在單個分析上下文中處理 pub 工作區中的所有套件,這與以前每個套件單獨使用一個上下文的行為不同。對於大型儲存庫,這可以顯著減少 Dart 語言伺服器消耗的記憶體量,從而提高 IDE 效能。

要定義 pub 工作區,請在根 pubspec.yaml 檔案中加入 workspace 欄位,並列出相關套件,如下例所示:

1
2
3
4
5
6
7
8
name: _
publish_to: none
environment:
sdk: ^3.6.0
workspace:
- packages/helper
- packages/client_package
- packages/server_package

然後在儲存庫中的任何位置執行 pub get 以完成映射和剩餘的檔案管理。

要使用 pub 工作區,所有工作區套件(但非相依性)必須具有 ^3.6.0 或更高版本的 SDK 版本約束。

有關更多資訊,包括完整的詳細說明和邊緣案例涵蓋範圍,請查看 Pub 工作區(monorepo 支援) 頁面。

Pub 下載次數

我們聽取了您對 pub.dev 上更精確指標的 請求,現在下載次數就在這裡!下載次數取代了單個套件頁面上之前的「人氣分數」。除了新的指標之外,您還將在每個頁面上找到一個迷你圖表,顯示每週的下載次數。

螢幕截圖突出了 pub.dev 上新的下載次數和迷你圖表

您可以在 pub.dev 評分文件 中了解更多關於下載次數的資訊。

數字分隔符

Dart 語言現在允許使用底線 (_) 作為數字分隔符,這有助於提高長數字字面量的可讀性。多個連續的底線表示更高級別的分組。以下是一些 Dart 中有效使用數字分隔符的範例:

1
2
3
4
1__000_000__000_000__000_000
0x4000_0000_0000_0000
0.000_000_000_01
0x00_14_22_01_23_45

請記住,數字分隔符僅在數字 之間 有效,因此不允許使用 100_、0._000_1 或 1.2e_3 之類的字面量。始終如一地使用數字分隔符也很重要,因此請確保數字組在每個級別的組之間大小相同。

要使用數字分隔符,您需要將套件中的最低 SDK 提升至 3.6.0 或更高版本。

結語

今天就到這裡。有關 Dart 團隊在本季度所做工作的完整故事,請查看 3.6.0 的變更日誌。別忘了查看 Flutter 3.27 版本部落格 文章以獲取更多更新。明年見!


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

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

使用 Impeller、Cupertino 更新等提升應用程式開發

簡介

讓我們深入了解 Flutter 3.27!此版本充滿了增強功能和特性,著重於改進您的開發體驗並提升應用程式的效能。

我們在架構、引擎和生態系統方面都進行了更新,包括 Impeller 的進展和 Cupertino widget 的改進。此外,我們還在 DevTools 中引入了令人興奮的新功能,讓管理 Flutter 專案比以往任何時候都更容易。Flutter 3.27 包含來自 187 位貢獻者(包括 49 位首次貢獻者!)的超過 1,400 個提交,證明了 Flutter 社群令人難以置信的活力和合作。讓我們一起探索新功能吧!

架構

Cupertino 更新

此版本包含對 CupertinoCheckboxCupertinoRadio 的高保真度更新,其中包括對大小、顏色、筆劃寬度和按下時的行為的更新。CupertinoRadioCupertinoCheckboxCupertinoSwitch 也進行了大量的可自訂性更新,加入了滑鼠游標、語義標籤、縮圖和填充顏色等屬性。某些屬性(例如 CupertinoCheckbox 的非活動顏色)已被棄用,而其他屬性(例如 CupertinoSwitch 的軌跡顏色)則已重新命名。這些變更使這些 widget 與其 Material 對應項的功能齊平。

CupertinoSlidingSegmentedControl 的拇指半徑、分隔線高度、填充、陰影和拇指縮放對齊方式也進行了一些保真度更新。它現在還支援停用個別區段,以及根據區段內容進行比例佈局。

感謝社群成員 @veloce 的出色貢獻,CupertinoNavigationBarCupertinoSliverNavigationBar 的背景現在是透明的,直到內容在其下方捲動。

這使得 sliver 導航欄在其展開狀態下可以擁有與其背景相同的顏色,但在其摺疊狀態下可以擁有不同的可自訂顏色(並且能夠在捲動時在這兩種顏色之間進行插值)。

感謝社群成員 @kerberjg 的出色貢獻,您現在可以使用 CupertinoButton 中新的 sizeStyle 屬性,透過新的 CupertinoButtonSize 列舉來應用 iOS 15+ 按鈕樣式。使用新的 CupertinoButton.tinted 建構函式來製作具有半透明背景的按鈕。

CupertinoButton 還有一個新的 onLongPress 處理程式,並且允許透過鍵盤快捷鍵執行操作。

CupertinoPickerCupertinoDatePicker 現在將捲動到點擊的項目。

CupertinoAlertDialog 現在支援點擊滑動手勢。

CupertinoActionSheet 進行了一些高保真度更新,包括在所有系統文字大小設定中調整填充和字體大小,以及在滑過按鈕時支援觸覺回饋。

其他變更包括:CupertinoContextMenu 現在支援捲動(如果其動作超出螢幕範圍),CupertinoDatePicker 不再裁剪其欄中的長內容,以及 CupertinoMagnifier 透過提供放大比例來支援縮放效果。

標準化 Material 主題

在此版本中,CardThemeDialogThemeTabBarTheme 已經過重構,以使其符合 Flutter 的元件主題規範。加入了 CardThemeDataDialogThemeDataTabBarThemeData 來定義元件視覺屬性預設值的覆蓋。未來的版本將繼續標準化像這樣的元件主題,以便在 Material 函式庫中提供更一致的主題體驗。

CarouselView 的更多功能

此版本引入了 CarouselView.weighted,可以在 Carousel 中啟用更動態的佈局。透過調整建構函式中的 flexWeights 參數,您可以實現各種項目佈局。例如,[3, 2, 1] 建立 多瀏覽 佈局,[7, 1] 建立 英雄 佈局,而 [1, 7, 1] 建立 居中英雄 佈局。這些值表示每個項目在 Carousel 視圖中佔據的相對權重,並且可以自訂以滿足使用者的特定需求。

Material 中的更多內容

此版本包含許多小的功能增強和錯誤修復。一些增強功能範例:

  • SegmentedButton 現在有一個 direction 屬性,因此項目可以垂直對齊。
  • ButtonStyleButton 類別(ElevatedButtonFilledButtonOutlinedButtonTextButtonMenuItemButtonSubmenuButton)的 styleFrom 方法中加入了更多與圖示相關的屬性,以允許更多自訂。

一些錯誤修復範例:

  • ButtonStyleButton 類別的圖示大小和顏色預設值與 Material 3 規範一致。
  • AppBar 的捲動下行為現在可以在導航抽屜打開時正確保留,與原生 Android 體驗相符。
  • MenuAnchor 透過焦點修復得到了進一步改進,並且解決了幾個 DropdownMenu 問題,包括巢狀捲動元件中的捲動問題和過濾機制行為。

混合路由轉場

當先前的路由和新的路由具有不同的頁面轉場時,ModalRoutes 現在更加靈活。當新的路由進入螢幕時,有時它們需要先前的現有路由播放特定轉場以與新路由的進入轉場同步。現在,ModalRoutes 可以互相提供退出轉場建構函式,以便進入和退出轉場始終同步。這允許頁面使用 Flutter 的 NavigatorRouter 在一個頁面上具有多個路由轉場選項。

文字選取改進

Flutter 的 SelectionArea 現在支援在 Linux、macOS 和 Windows 上使用 Shift + 點擊手勢將選取範圍的末尾移動到點擊位置。

您現在還可以透過 SelectableRegionState 上的 clearSelection 方法清除 SelectionAreaSelectableRegion 下的選取範圍。現在還可以透過提供 GlobalKey 並存取其 SelectionAreaState.selectableRegion 來透過 SelectionArea 存取 SelectableRegionState

我們還解決了 RenderParagraph 的一些問題,因此在調整 視窗大小 後,以及在點擊或 點擊實際文字 [之外] 時,在 SelectionAreaSelectableRegion 下選取文字仍然可以按預期工作。

Row 和 Column 間距

此版本還提供了一個使用 RowColumn 的新便利方法:spacing!此雙精度參數將應用固定數量的像素來偏移 RowColumn 的子項之間的距離,從而減少先前執行此操作所需的程式碼量。

引擎

Android 上的 Impeller

我們很高興地宣布,Impeller 現在是現代 Android 設備上的預設渲染引擎。雖然自 Flutter 3.16 穩定版本於 2023 年 11 月發佈以來,這些設備上的 Impeller 一直處於預覽階段,但從那時起,我們根據使用者回饋對效能和保真度都進行了重大改進。

在較舊的 Android 設備和不支援 Vulkan 的設備上,仍然會像以前一樣使用 Skia 渲染引擎。雖然我們相信 Impeller 的效能和保真度都非常高,但使用者可以選擇退出並返回到傳統的 Skia 渲染器,方法是將 --no-enable-impeller 傳遞給 flutter 命令列工具,或者將以下內容放入您的 AndroidManifest.xml 檔案中:

1
2
3
<meta-data
android:name="io.flutter.embedding.android.EnableImpeller"
android:value="false" />

接下來,我們將繼續改進 Impeller 在 Android 上的效能和保真度。此外,我們打算讓 Impeller 的 OpenGL 後端做好生產準備,以移除 Skia 後備。

如同在預覽期間一樣,我們請求 Flutter 開發人員升級到最新的穩定版本,並提交在啟用 Impeller 時發現的任何缺點的錯誤。回饋對於確保 Impeller 在 Android 上取得成功仍然至關重要。Android 硬體生態系統比 iOS 生態系統更加多元。因此,關於 Impeller 最有用的回饋應包括有關發生問題的特定設備和 Android 版本的詳細資訊。

iOS 設備的改進

在以前的 Flutter 版本中,使用者可能會遇到 一個問題,即 iOS 設備上的應用程式在合成器反壓下每幀等待幾毫秒。這種反壓將被視為柵格工作負載開始時的延遲。在許多情況下,這種延遲會導致畫面丟失和卡頓。這種效應在具有較小畫面時間預算的高畫面播放速率設備上尤為明顯。

在此版本中,開源貢獻者 @knopp 加入了一個 新的實作 Metal 渲染表面,允許柵格工作負載在 UI 工作負載完成後更一致地開始。這意味著使用者將觀察到整體畫面時間更加穩定,因為在 iOS 系統合成器上的等待時間更少。特別是,Flutter 現在將在高畫面播放速率設備上更一致地達到 120Hz。在我們的基準測試中,我們觀察到在許多情況下平均畫面柵格化時間都大幅減少了。

此圖表顯示在 iPhone 11 設備上運行的許多不同基準測試中,平均畫面柵格化時間(以毫秒為單位)全面下降。

架構廣色域顏色

Flutter 現在支援使用 DisplayP3 色彩空間中的顏色來定義 UI,以充分利用 iOS 設備上的顯示器。它還具有更寬的位元深度,以支援 sRGB 和 DisplayP3 色彩空間中的更多顏色。此變更導致一些 Color 方法被棄用;更多資訊可以在 重大變更通知 中找到。

Web

Web 改進

此版本為 Flutter Web 帶來各種改進,著重於效能、效率和輔助功能:

  • 現在,Safari 和 Firefox 中的圖像解碼使用 <img> 元素完成,而不是對所有靜態圖像使用 WebAssembly 編解碼器。這消除了與圖像解碼相關的卡頓,並減少了 WASM 記憶體使用量。
  • 平台視圖經過優化,以減少畫布覆蓋層的數量,從而提高渲染效率。
  • Flutter 團隊開發的所有插件和套件現在都與 WebAssembly 相容。
  • 對標題、對話框、密碼、iOS 鍵盤、連結和捲動元件實作了多項輔助功能修復。
  • 在 CanvasKit 和 Skwasm 渲染器中修復了許多渲染錯誤,包括圖像濾鏡、裁剪和 Paint 物件的記憶體使用量。
  • 改進了多視圖模式下的拖動捲動。

iOS

Swift Package Manager

Flutter 正在遷移到 Swift Package Manager。這帶來了許多好處:

  1. 存取 Swift 套件生態系統。Flutter 插件將能夠利用不斷發展的 Swift 套件 生態系統!
  2. 簡化 Flutter 安裝。Swift Package Manager 與 Xcode 捆綁在一起。未來,您將不再需要安裝 Ruby 和 CocoaPods 來為 Apple 平台使用 Flutter。

以前,Swift Package Manager 支援僅在 Flutter 的「main」頻道上可用。我們很高興地宣布,Swift Package Manager 功能現在也在「beta」和「stable」頻道上可用!目前,Swift Package Manager 功能預設為關閉,因為我們正在解決一些問題。

鼓勵插件作者將 Swift Package Manager 支援加入到您的插件中。許多流行的插件——包括 Firebaseplus 插件更多——已經遷移到 Swift Package Manager!

Pub.dev 現在會檢查插件的 Swift Package Manager 相容性。未來,不相容的套件將不會獲得完整的套件評分。

如果您對 Flutter 對 Swift Package Manager 的支援有任何回饋,請 提交問題

Android

邊緣到邊緣

在運行 Android 15+ 的設備上,您的應用程式預設將使用邊緣到邊緣模式並以全螢幕運行!有關此變更以及如何選擇退出此行為的詳細資訊,請參閱指南

支援 Freeform

Android Freeform 允許使用者調整應用程式視窗的大小,並且一直作為開發人員選項提供。Flutter 的 SafeAreaMediaQuery 已更新,以便在 freeform 視窗移動到硬體缺口時處理硬體缺口。

Kotlin 建置腳本支援和宣告式插件遷移

開發人員現在可以使用 build.gradle.kts 檔案。Flutter 工具現在支援 Kotlin 建置檔案。如果您遇到我們不支援 Kotlin 的地方,請提交錯誤。Groovy 仍然是支援的 Gradle 語言。

Flutter 3.27 是最後一個支援 Gradle 的傳統 apply 腳本方法 的版本。請參閱遷移文件

注意:大多數應用程式需要手動遷移。新建立的應用程式將 使用 kotlin 建置檔案

遷移到 Flutter Gradle 插件的宣告式包含有助於 Flutter 遷移到 Kotlin 建置檔案,從而提高類型安全性,並更容易維護建置程式碼的單元測試。這也是在 Android 工具版本之間帶來更多穩定性的一項更廣泛工作的一部分。

生態系統

Flutter 和 Dart 套件生態系統峰會美國 2024

作為 2024 年 9 月 Fluttercon USA 的一部分,我們舉辦了 Flutter 和 Dart 套件生態系統峰會。這是我們的第二次現場峰會,繼 2024 年 7 月 Fluttercon Europe 的峰會和我們於 2023 年 8 月舉辦的第一屆虛擬峰會之後。請參閱此處的討論會重點摘要 連結

峰會匯集了套件作者和維護者,進行了以下主題的非會議式會議:

  • 會議 1 - 加入對 Swift Package Manager 的支援
  • 會議 2 - 原生互操作的過去、現在和未來
  • 會議 3 - 可持續的套件維護模型

我們期待與社群合作,在未來舉辦更多此類峰會。

pub.dev 上的套件下載次數

我們在 pub.dev 上推出了下載次數指標!

pub.dev 上提供更精確的受歡迎程度指標一直是該網站問題追蹤器中 請求最多的 功能。

現在,當您查看套件頁面時,您將看到一個 30 天下載次數,取代了以前的「受歡迎程度分數」。

此外,我們還加入了一個互動式折線圖,顯示隨著時間推移的每週下載活動。此圖表可幫助開發人員和套件作者發現套件使用趨勢。例如,它可能表明新版本導致使用量激增,或者套件正在變得越來越流行或越來越不受歡迎。

每次從 pub.dev 下載套件時都會註冊下載次數。例如,當您運行 flutter pub get 且套件尚未在您的 pub 快取中時。

顯示的下載次數是原始下載次數,反映了來自 CI 系統、套件分析、人工 pub 用戶端使用者等的使用情況。

Pub 工作區

我們將在 Dart 3.6 中推出 Pub 工作區,以支援在一個 monorepo 中開發多個相關套件。

透過定義引用儲存庫中其他套件的根 pubspec,在儲存庫中的任何位置運行 pub get 將會為所有套件產生共用解析。這可確保所有套件都使用一組一致的相依開發。

分析器也會選取共用解析,現在分析器只需追蹤整個工作區的單個分析上下文,從而可能在 IDE 中打開整個儲存庫時顯著減少記憶體使用量。

從 GitHub 自動發佈 Flutter 套件

我們擴展了 setup-dart 發佈 Github Actions 工作流程,還允許將 Flutter 套件 自動發佈 到 pub.dev,從而更容易以可驗證的方式為 Pub 生態系統做出貢獻。

推出互動式媒體廣告插件

我們推出了 互動式媒體廣告 (IMA) Flutter 插件,可以輕鬆地將多媒體廣告整合到使用 Flutter 建置的 iOS 和 Android 應用程式中。IMA 插件可以從任何符合 VAST 標準的廣告伺服器請求廣告,並在您的應用程式中管理廣告播放。廣告在位於應用程式內容影片播放器頂部的單獨影片播放器中播放。目前支援的功能包括:

  • 線性串流影片廣告請求的用戶端 API
  • VAST 和 VMAP
  • 單個和 Podded 請求
  • 可跳過廣告

應用程式內購買 iOS 和 macOS 插件更新

我們已將 StoreKit 2 支援加入到 in_app_purchase_storekit 套件中,以從 iOS 18 中棄用的 StoreKit 1 API 遷移。這使我們能夠在未來加入新的 StoreKit 2 功能,例如更好的訂閱管理。敬請期待即將推出的遷移指南,以説明您轉換到新的 StoreKit 2 功能。這是一個非重大變更,因此您可以隨時遷移。

DevTools 和 IDE

Flutter DevTools 更新

此版本包含新功能、整體穩健性和完善性改進,以及一些令人興奮的新的實驗性功能,您可以從 DevTools 中啟用這些功能。有關這些功能的更多資訊,請參閱下面的「預覽和實驗性功能」部分。

首先,我們在 Flutter Deep Links 工具中加入了對驗證 iOS 深度連結設定的支援。您現在可以驗證 Android 和 iOS 的深度連結。

DevTools Deep Links 工具中的 iOS 深度連結驗證。

接下來,我們對使用「離線」資料的工作流程進行了一些改進。有時需要將您在 DevTools 中查看的資料匯出以供將來使用或載入到其他工具中。此版本加入了將網路資料匯出為 .har 檔案的支援,以及將記憶體快照載入到 DevTools 中以便在 DevTools 未連線到正在運行的應用程式時查看的支援。

此外,如果您曾經在使用 DevTools 除錯記憶體問題時因為應用程式崩潰(可能是由於您正在嘗試除錯的 OOM 問題)而丟失了記憶體工具資料,我們感同身受!我們已透過允許您即使在應用程式斷開連線後仍然可以在 DevTools 中繼續查看最新的記憶體工具資料來修復此使用者體驗痛點。

這些只是此版本中的一些重點。若要進一步了解 Flutter 3.27 中包含的所有更新,請查看 DevTools 的發行說明 2.38.02.39.02.40.1

預覽和實驗性功能

Flutter DevTools - 新的 Flutter Inspector

Flutter Inspector 已經過一些重大變更,以提高可用性並增強您的 UI 除錯體驗。透過切換「新的 Inspector」設定來啟用新的 Inspector。

DevTools 中的新 Flutter Inspector。

一些變更包括:

  • 簡潔的 Widget 樹,可以更輕鬆地查看深度巢狀的 Flutter widget 樹。當從 IDE 中使用 Flutter Inspector 時,這尤其有用,因為水平螢幕空間有限。
  • 一個選項,用於切換是否應在 widget 樹中包含實作 widget。實作 widget 是您未在應用程式程式碼中包含的 widget,而是由 Flutter 架構或其他套件加入到 widget 樹中的。
  • 選定 widget 的詳細資訊視圖,顯示內聯佈局查看器、widget 和渲染物件屬性,以及 Flex widget 及其子項的 Flex 佈局資源管理器。

新的 Inspector 很快就會有更多改進,但這些變更已經可以試用了!請提交您遇到的任何 問題,以便我們在預設啟用新的 Inspector 之前修復它們。

Flutter DevTools - 使用 WebAssembly 進行嘗試

在 DevTools 設定中啟用 WebAssembly 功能,以載入 WASM 編譯的 DevTools Web 應用程式。

這應該比預設的 JS 編譯版本的 DevTools 產生更好的效能。此功能是實驗性的,因此請提交您在 WASM 建置中遇到的任何 問題

DevTools 設定,用於使用 WebAssembly 建置而不是 Javascript 建置。

重大變更和棄用

Material Design 3 標記

最新的 Material Design 3 標記 (v6.1) 已應用於 Flutter Material 函式庫。Material Design 標記更新了僅在亮模式下 4 種顏色角色的映射,以在保持可存取對比度的同時更具視覺吸引力。我們在測試此變更時沒有發現任何損壞,此變更應用於以下顏色屬性:

  • 主要容器上(Primary10 到 Primary30)
  • 次要容器上(Secondary10 到 Secondary30)
  • 三級容器上(Tertiary10 到 Tertiary30)
  • 錯誤容器上(Error10 到 Error30)

這會影響使用這些角色作為其預設值的 Widget。

晶片(ChipActionChipChoiceChipFilterChipInputChip)的邊框顏色已從 ColorScheme.outline 更新為 ColorScheme.outlineVariant。這改善了晶片和按鈕之間的視覺層次結構。

Objective-C iOS 專案

自 2019 年 Flutter 1.9.1 發佈以來,新的 iOS 專案預設使用 Swift。建立 新的 Objective-C iOS 專案現在已被棄用,並且 flutter create --ios-language objc 標誌將在 Flutter 的未來版本中移除。您仍然可以打開 Xcode 專案並加入 Objective-C 檔案,包括插件的檔案。Java 繼續透過 flutter create --android-language java 標誌支援 Android 應用程式。

深度連結預設標誌

Flutter 的深度連結標誌的預設值已從 false 變更為 true,這意味著深度連結現在預設為選擇加入。如果您使用 Flutter 的預設深度連結設定,則不再需要手動將標誌設定為 true

但是,如果您使用協力廠商插件進行深度連結,例如:

此更新將引入重大變更。在這種情況下,您需要手動將 Flutter 深度連結標誌重置為 false

有關更多詳細資訊,請參閱設計文件:flutter.dev/go/deep-link-flag-migration

在 IDE 中棄用對較舊 SDK 的支援

為了確保我們能夠繼續提供高品質的開發人員體驗,我們正在對 IDE 插件支援進行變更。隨著 VS Code 和 IntelliJ 的 Dart 和 Flutter 插件的發展,支援較舊的 Dart SDK 版本變得越來越具有挑戰性。

從 Dart 3.6 發佈開始,我們將棄用對早於 3.0(於 2023 年 5 月發佈)的 Dart SDK 版本的支援。這意味著雖然這些工具可能仍然可以使用較舊的 SDK,但我們將不再為特定於這些版本的問題提供官方支援或修復。

隨著 Dart 3.7(預計在 2025 年第一季度)的發佈,我們將完全移除對這些較舊 SDK 版本的支援。這些插件的未來版本可能與這些版本不相容。

這對您意味著什麼?

  • 如果您使用的是較舊的 Dart SDK,我們鼓勵您升級到 3.0 或更高版本,以繼續接收支援和更新。
  • 此變更影響不到我們 1% 的使用者,它將使我們能夠專注於為絕大多數開發人員提供最佳的體驗。

結束語

有關此版本中的所有重大變更,請參閱 重大變更頁面 上的完整遷移指南列表。

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

結論

非常感謝整個 Flutter 社群使此版本成為可能!您的貢獻、回饋和熱情是推動 Flutter 向前發展的動力。我們對 Flutter 的未來感到無比興奮,並迫不及待地想看到您使用 Flutter 3.27 建置的出色應用程式。

有關所有變更的完整概要,請查看詳細的 發行說明和變更日誌。準備好體驗最新最好的功能了嗎?只需運行 flutter upgrade 並開始建置!


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

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

收看 #FlutterInProduction - 即將在您附近的螢幕上播出!

在您的日曆上標記,並務必在 2024 年 12 月 17 日 太平洋時間上午 11:00 / 東部時間下午 2:00 / 中歐時間晚上 8:00 收看 #FlutterInProduction

這個在 Flutter YouTube 頻道 上舉辦的線上活動與我們之前的活動略有不同。我們不僅會深入探討最新版本(儘管我們一定會這樣做!):#FlutterInProduction 還將慶祝我們作為一個社群共同走過的不可思議的旅程,使 Flutter 成為今天這個強大的、可投入生產的框架。

預期內容

  • #FlutterInProduction:無論您是經驗豐富的專業人士還是剛剛起步,您都將從 Flutter 使用者故事中獲得新的見解和靈感,從 Flutter 技術負責人那裡了解更多關於最新功能的資訊,並在此多環節線上活動中了解 Flutter 應用程式開發的未來。
  • #AskFlutter 線上問答:從 Flutter 團隊負責人那裡獲得您的問題的解答。請持續關注我們在社群媒體上的徵集問題!
  • Observable Flutter:對在企業開發環境中使用 Flutter 構建有什麼疑問嗎?從在生產中使用 Flutter 的公司的技術負責人那裡獲得這些問題的解答。

保持關注

我們將會有一些驚喜和公告,因此請務必採取以下步驟以保持了解最新資訊。

  1. 在您的日曆上或在 YouTube設定提醒,了解活動時間。
  2. TwitterLinkedIn 或此處的 Medium 上關注我們!

我們迫不及待地想與您分享 #FlutterinProduction


收看 #FlutterInProduction - 即將在您附近的螢幕上播出! 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

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

Prospera:使用 Flutter 將 AI 帶到銷售前線

Gemini graphic

世界各地的開發人員正在使用 Google 的 Gemini 模型來應對現實世界的挑戰,而我們看到最令人興奮的應用之一就是在銷售領域。認識 Prospera,它在我們最近的 Gemini API 開發者競賽 中獲得了 最佳 Flutter 應用程式最有用的應用程式 兩項大獎!Prospera 由來自德國的 Leon Kukuk 和 Max Hasenohr 共同開發,它是一款 AI 驅動的銷售教練,可在通話期間和通話後提供即時指導和回饋。這款創新的 Flutter 應用程式展示了 Gemini 如何改變銷售專業人員學習和提升技能的方式。

Gemini API 作為核心:即時分析和多模態魔法

Prospera 以真正獨特的方式利用 Gemini 的多模態功能。透過將語音對話片段直接輸入 Gemini API 的 Python SDK,該應用程式可以從經驗豐富的銷售教練的角度提供即時回饋。想像一下,在您通話期間,有一個 AI 副駕駛會提供有價值的見解和建議——這就是 Prospera 的力量。

但他們是如何實現這種無縫整合的呢?Prospera 的開發者 Leon 巧妙地使用了 Dart FFI(外部函數介面)和 ffigen 套件,為 PJSIP C 函式庫建立了 Dart 繫接,從而在 Flutter 中實現了無縫的電話整合,並允許透過 Gemini API 處理這些通話。這使他們能夠即時處理音訊資料,並向銷售代表提供即時回饋。

「Gemini 對語音的細緻理解給我們留下了深刻的印象,這要歸功於其多模態音訊功能以及易於實作的特性。Flutter 使我們能夠以驚人的速度開發跨平台應用程式,同時提供了透過 FFI 和平台通道運行原生程式碼的強大機會。」——Leon

適用於所有平台的 Flutter 應用程式

Prospera 使用 Flutter 構建,可以從單一程式碼庫在各種桌面平台上無縫運行。這不僅簡化了開發,還為未來擴展到行動裝置打開了大門,讓更廣泛的受眾可以使用該應用程式。開發人員還密切關注使用者體驗,使用 Material Design Widget 製作了一個設計精美的應用程式,並確保了完全的無障礙使用。

超越銷售:展望溝通的未來

透過將 Gemini 的強大功能與 Flutter 的多功能性相結合,Leon 和 Max 開發了一款有可能徹底改變我們學習、互動和在職業生涯中脫穎而出的方式的應用程式。Prospera 不僅僅是一個銷售教練;它讓我們看到了 AI 如何增強人類溝通和專業發展的未來。雖然 Prospera 專注於銷售指導,但其底層技術和方法具有更廣泛的應用。分析即時對話並提供建設性回饋的能力在各種以溝通為導向的職業中都很有價值,例如客戶服務和教育。

想了解更多關於 Dart FFI 以及它如何增強您的 Flutter 應用程式的資訊嗎?請在 pub.dev 上查看 ffigen 套件。


Prospera:使用 Flutter 將 AI 帶到銷售前線 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

Google 暑期程式碼計劃 2024 成果

Google 暑期程式碼計劃 (GSoC) 的重點是讓全球新的開發人員參與開源軟體開發。Google 贊助貢獻者在夏季與輔導開源組織合作進行為期 12 週的程式設計專案。在過去的 19 年裡,已有超過 20,000 名貢獻者參與了 Google 暑期程式碼計劃。

2024 年,Dart 連續第五年作為輔導組織參與 Google 暑期程式碼計劃。今年,我們收到了來自世界各地潛在參與者的 50 份專案提案。今天,我們重點介紹了今年夏天我們輔導的一些專案。貢獻者在此文章中描述了他們的專案。

使用 OkHttp 的 JNI 綁定的 HTTP 客戶端

作者:Anikate De

現有的 Dart HTTP 客戶端無法與原生 Android API 互動,限制了對 KeyStore 私鑰、系統代理和使用者安裝憑證的支援(所有這些都是 Dart 使用者強烈要求的功能)。

開源專案 OkHttp 是 Android 上網路密集型應用程式的熱門選擇,它擁有的 API 可以無縫地實作上述功能。因此,OkHttp 被選為此專案的起點。使用 GSoC 2022 專案,package:jnigen,為多個 OkHttp API 生成了 Dart 的 JNI 綁定。OkHttpClient 與現有的 HTTP 客戶端完全相容,允許從任何替代方案無縫過渡到使用 OkHttpClient。

除了非同步 HTTP 請求之外,package:ok_http 還支援 WebSockets,這需要通過 package:web_socket_conformance_test 中的所有測試。這允許從任何 WebSocket 替代方案平穩過渡。使用 package:ok_http 基準測試的 Flutter 應用程式報告了所有其他 Dart 提供的 HTTP 客戶端中最小的 APK 大小。

在您的 Flutter 專案中利用 OkHttpClient 的優點就像匯入它一樣簡單,並編輯您目前的 Client 物件!

GSoC 貢獻者報告 中閱讀更多關於專案的詳細資訊。

Swift2ObjC:ObjC 包裝器產生器

作者:Mohammad Eid

Dart 可以(間接)與 Swift 程式碼通訊。這是透過 ObjC 完成的,因為 Dart 可以使用 ffi 套件與基於 C 語言(如 ObjC)通訊。Swift API 可以使用 @objc 註釋,以請求編譯器為它們產生 ObjC 標頭。使用 ffigen 自動為 Dart 產生 ffi 綁定。對於 Swift,您需要手動註釋您想要與之通訊的 API。

註釋 Swift API 可能比加入 @objc 註釋更複雜。如果您不擁有該模組怎麼辦?如果您無法編輯它,您必須建立一個自訂包裝器來鏡像 API,以便您可以註釋方法。此外,由於 Swift 是一種現代語言,而 ObjC 不是,您可能會遇到一些 API,這些 API 無法直接在 ObjC 中表示,因此您也必須解決這些情況。

Swift2ObjC 為您處理這些事情:它為 Swift 模組產生包裝器,並使用 @objc 註釋它們,同時確保所有 API 都可以在 ObjC 中表示 - 否則 Swift 模組將無法編譯。我在 Medium 上的這篇文章 (文章連結) 中解釋了實作細節(如果您正在考慮為該工具做出貢獻,可以參考這篇文章,因為它仍在積極開發中)。您還可以在 GitHub 上的 swift2objc 儲存庫 中深入研究原始碼。

使用 dartdoc_test 測試文件註釋

作者:Takuma Ichikawa

Dart 允許在文件註釋中嵌入程式碼範例。這是一個有益的做法,因為它也會作為 API 文件發佈,並以套件使用者和開發人員都容易理解的方式展示如何使用套件。但是,文件註釋中的程式碼範例不會被分析或測試,因此很難發現錯誤,並且難以維護。隨著專案和文件的增長,這個問題變得更加嚴重。

因此,對於 GSOC,我開發了 dartdoc_test 套件來解決此類問題。dartdoc_test 工具可以提取和分析文件註釋中的程式碼範例,以發現潛在的錯誤。透過將套件加入到您的專案中,並從命令列運行它,您可以發現所有程式碼範例中的錯誤。您還可以在運行 dart test 命令時,透過加入 dartdoc_test 測試檔案來執行程式碼範例的靜態分析。

透過運行以下兩個命令,您可以輕鬆地在 Dart 專案中試用 dartdoc_test。

1
2
dart pub add dev:dartdoc_test
dart run dartdoc_test

要了解更多資訊,請在 pub.dev 上查看 dartdoc_test

使用 FFIgenPad 在 Web 上使用 WASM 產生 dart:ffi 綁定

作者:TheComputerM

Dart 作為一種可以編譯成各種目標的語言,包含了促進與原生二進位制檔案通訊的功能。您可以使用 dart:ffi 等套件與 C API 互動,並使用 package:jni 與 Java 程式碼互動。您只需編寫一些黏合程式碼來指定您正在呼叫的原生函數的類型簽章。

當您使用一個具有許多匯出的函式庫時,問題就出現了,手動編寫太多匯出。這個問題可以使用 ffigen(一個先前的 GSoC 專案)來解決,這是一個僅使用函式庫的標頭檔來產生綁定的工具。要做到這一點,您必須安裝 ffigen,設定所有正確的依賴項和需求,然後(可能)一次又一次地迭代。這並不是無縫且簡單的定義。因此,FFIgenPad 誕生了。

該專案的另一個目標是測試 dart2wasm 編譯器(您可以在 此部落格文章 中找到我的實驗)。當然,我們可以在伺服器上運行 ffigen,並在其上放置一個 UI。相反,我透過將 libclang 和 ffigen 都編譯成 WebAssembly 來確保 FFIgenPad 完全在瀏覽器上運行(當您可以使用客戶端的系統時,為什麼還要使用伺服器進行計算)。

https://ffigenpad.surge.sh/ 查看演示,並在 GitHub 上的 我的分支 中查看原始程式碼。我要感謝我的導師 Prerak Mann、Daco Harkes 和 Jackson Gardner,感謝他們在我請求幫助時隨時提供協助。


Google 暑期程式碼計劃 2024 成果 最初發佈在 Dart 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

宣布 Dart 3.5 和 Dart 路線圖更新

我們又到了季度 Dart SDK 發佈的時間。我們在互操作性方面進行了改進,在我們的 pub.dev 套件管理器中加入了新功能,並且我們已將新的 Web 整合 API 升級到穩定版和 1.0 版。

我們的大部分時間都花在更大、跨多個季度的努力上,因此我們也更新了 Dart 路線圖,詳細說明了我們希望在未來幾個季度取得的進展。

Dart 3.5 發佈圖片。

Dart 3.5 的新功能

Dart 3.5 具有以下討論的許多新功能。核心函式庫 API 也有一些較小的變更,以及大約 10 個非常小的重大變更,這些變更涵蓋在 變更日誌 中。

Web 平台和 JS 互操作性

在 Dart 3.4 和 Flutter 3.22 中,我們引入了對 將 Flutter Web 應用程式編譯為 WebAssembly 的支援。編譯為 WebAssembly 需要使用我們新的 Dart 到 JS 互操作模型,該模型之前處於預覽階段。從 Dart 3.5 開始,它現在被認為是穩定且完整的,我們已將 package:web(它取代了舊的 dart:html 函式庫)中的瀏覽器 API 繫結更新到 1.0 版。

我們鼓勵所有 Web 套件作者 遷移到 package:web。我們計劃在下一個 Dart 版本中棄用舊的互操作 API(dart:html、dart:js、package:js 等),並在明年晚些時候完全停止使用它們。我們邀請您在 追蹤議題 中就此計劃提供回饋。我們還計劃更新 pub.dev 套件管理器上的 評分,以獎勵支援新互操作模型的 Web 套件。

我們還加入了 一個新的 lint,它驗證您的程式碼是否正確使用了新的 JS 互操作類型。我們建議您在遷移 Web 套件時將此 lint 加入到您的 analysis_options.yaml 檔案中。

Dart 原生互操作性

我們還對原生互操作性進行了一系列改進,它支援從 Dart 直接調用到 C、Java、Kotlin、Objective-C 和 Swift。

C 互操作由我們的 FFI(外部函式介面)函式庫啟用,我們已經支援了幾年。在 Dart 3.5 中,我們進行了漸進式改進,以支援將 Dart TypedData 物件的指標直接傳遞給 FFI,避免必須先將記憶體從 Dart 複製到原生(詳細資訊)。

Java 和 Kotlin 互操作由 JNIgen 生成器(目前處於預覽階段)啟用,它自動建立繫結程式碼,以透過 Java 原生介面 (JNI) 從 Dart 調用到 Java 和 Kotlin。我們改進了效能,並加入了對 Java 異常和 Kotlin 頂層函式的支援。我們還停止了以前的 基於 C 的繫結,因為現在替代的僅 Dart 繫結具有相當的效能和功能,並且更容易使用。有關詳細資訊,請參閱 變更日誌

Objective-C 互操作建立在 FFI 和我們的 FFIgen 生成器(目前處於預覽階段)之上。我們加入了對 Objective-C 協議和常見類型(例如 NSString)的支援。有關使用 FFIgen 建立的套件的大型範例,請參閱 cupertino_http,它與 Apple 的 URL Loading System 網路函式庫互操作。

我們將繼續在未來的版本中進一步投資互操作性——無論是在完成上述函式庫方面,還是在支援 Swift 方面。有關詳細資訊,請參閱下面的路線圖部分。

Pub.dev 套件儲存庫

Pub.dev 是我們的套件儲存庫,社群可以在其中分享和找到具有豐富功能的套件。我們在這裡進行了一些改進。首先,我們改進了對 主題 的支援:套件作者可以透過這種機制使用它們所屬的類別(例如 widget)來標記他們的套件。我們現在 整合 涵蓋相同類別但措辭略有不同的常見主題(例如 widgets vs widget)。

其次,我們加入了一個新的 pub unpack 命令。這提供了一種快速簡便的方法來將套件下載到您的檔案系統。例如,如果您想在本地機器上運行套件的範例程式,可以使用此命令:

1
2
3
4
5
6
7
8
$ dart pub unpack path
Downloading path 1.9.0 to `./path-1.9.0`...

$ cd path-1.9.0/example/

$ dart run example.dart
Current path style: posix
Current process path: /Users/mit/tmp/path-1.9.0/example

第三,我們加入了一個新的 pub downgrade --tighten 命令。這可以用於檢查套件相依中的所有版本限制。運行時,它會將較低的限制更新為 pub 能夠解析的最低版本。

Dart 路線圖更新

除了上述已完成的功能外,我們還在許多領域開展了工作,以推進我們的長期路線圖。

大型 monorepos 的 IDE 和分析器效能

「monorepo」是一種常見的程式碼結構方式,用於將一組相關套件和應用程式的程式碼放在單個儲存庫中,例如 Flutter 的 套件儲存庫。monorepo 不僅僅是為了方便將所有程式碼「放在一起」,而且還可以作為確保儲存庫中的各個套件和應用程式相互相容的關鍵工具。

我們一直收到在大型 monorepos 中工作的開發人員的回饋,認為我們的工具,尤其是分析器的效能可能不足。我們對這些問題的分析表明,根本原因是我們最終為每個套件及其所有相依載入了多個重疊的分析上下文,導致記憶體中同時存在多個套件分析副本。我們相信根本的解決方案是在此類儲存庫中建立每個相依版本的單個共用解析,並正在透過一個名為 workspaces 的新 pub 功能來開發此功能。我們將在下一個 Dart 版本中分享更多關於此的資訊,但現在您可以先看看這是如何 最近應用 於 Flutter 引擎儲存庫的。

Pub.dev 套件儲存庫

pub.dev 套件儲存庫的使用者長期以來一直要求改進 每個套件的使用/下載 指標。這對於套件作者來說,可以作為一個指標,表明有多少使用者從他們的工作中受益;對於套件使用者來說,可以作為一個指標,表明其他開發人員正在使用哪些套件。我們很高興地與大家分享,我們在這項功能上取得了良好的進展,並希望在年底前推出預覽版。

Dart 原生互操作

對於使用 JNIgen 的 Java 和 Kotlin 互操作,我們預計將在接下來的兩個季度內完成核心支援,並從實驗版升級到穩定版 1.0。有關詳細資訊,請參閱 JNIgen 追蹤器。對於 ObjectiveC 互操作,我們有一個類似的目標;請參閱 Objective-C 追蹤器

接下來,我們正在研究與 Swift 程式碼的直接互操作。初步實驗看起來很有希望,我們希望在明年年初加入實驗性支援。

原生互操作和原生程式碼的捆綁

在許多情況下,直接互操作用於調用到作業系統中存在的 API,這意味著這些 API 在這些主機平台上始終可用。但是,在某些情況下,Dart 互操作的程式碼是主機上未直接包含的原生程式碼,這對使用此類互操作的套件作者提出了一個實際挑戰:如何在不將大量手動步驟推給套件使用者的情況下捆綁和構建該原生程式碼?為了支援這一點,我們正在探索一個 原生資源系統,它可以支援發佈包含原生程式碼的 Dart 套件,以及一個標準化協議,以啟用 dart 和 flutter CLI 工具自動構建和捆綁該程式碼。我們預計這將啟用一組新的互操作使用案例,同時為使用依賴原生程式碼的套件的開發人員提供簡單的使用者體驗。

Dart 語言和巨集

Dart 語言和編譯器團隊目前的大部分時間都花在推進大型語言功能巨集上,我們在 Dart 3.4 部落格文章 中介紹了巨集。正如我們當時所說,這是一項巨大的任務,可能會在我們的一些核心使用案例(例如熱重載)中造成回歸,因此我們正在採取一種徹底的方法,並且在我們能夠分享下一步的詳細資訊之前,可能需要幾個季度的進一步工作。

除了巨集之外,我們還同時探索許多其他較小的語言功能,如 Dart 語言漏斗 中所述。

自去年秋季以來,我們一直在重寫 Dart 格式化程式。舊的設計在很多年裡都運行良好,但隨著 Flutter 的成功,我們希望轉向 一種新風格,這種風格更適合 Flutter 使用者經常撰寫的宣告式程式碼。舊的格式化程式無法產生這種輸出。重寫工作接近完成,很快就會發佈。如果您想試用它,請傳遞實驗旗標 tall-style(旗標說明)。如果您看到奇怪的輸出,歡迎提供 回饋

結語

這就是我們今天要分享的全部內容。我們歡迎您提供回饋,無論是關於討論的路線圖項目,還是關於 Dart 3.5 中的新功能,這些功能可以從 Dart.dev 獲取,或捆綁在今天的 Flutter 3.24 版本 中。


宣布 Dart 3.5 和 Dart 路線圖更新 最初發佈在 Dart 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

在 Flutter 中建立自訂渲染器並渲染 3D 場景。

Flutter 3.24 版本引進了一個新的低階圖形 API,稱為 Flutter GPU。還有一個由 Flutter GPU 支援的 3D 渲染函式庫,稱為 Flutter Scene(套件:flutter_scene)。Flutter GPU 和 Flutter Scene 目前都處於預覽階段,僅在 Flutter 的 主頻道 上可用(由於依賴實驗性功能),需要 啟用 Impeller,並且可能偶爾會引入重大變更。

本文包含這兩個套件的兩個「入門」指南:

  1. 🔺 進階:使用 Flutter GPU 入門
    如果您是經驗豐富的圖形程式設計師,或者您對低階圖形感興趣,並想在 Flutter 中從頭開始建立渲染器,那麼本指南將幫助您開始使用 Flutter GPU。您將從頭開始繪製第一個三角形… 在 Flutter 中!
  2. 💚 中級:使用 Flutter Scene 進行 3D 渲染
    如果您是想要將 3D 功能添加到應用程式中的 Flutter 開發人員,或者您想要使用 Dart 和 Flutter 建立 3D 遊戲,那麼本指南適合您!您將設定一個專案,在 Flutter 中匯入和渲染 3D 資產。

使用 Flutter GPU 入門

⚠️ 警告!⚠️ Flutter GPU 最終是一個低階 API。絕大多數將從 Flutter GPU 的存在中受益的 Flutter 開發人員很可能會透過使用在 pub.dev 上發佈的更高階渲染函式庫來做到這一點,例如 Flutter Scene 渲染套件。如果您對 Flutter GPU API 本身不感興趣,而只是對 3D 渲染感興趣,請跳到 使用 Flutter Scene 進行 3D 渲染

哦,真閃亮。這是一個射線行進的符號距離場。您可以使用 Flutter GPU 渲染它,但使用 [自訂片段著色器](https://docs.flutter.dev/ui/design/graphics/fragment-shaders) 也完全有可能。

使用 Flutter GPU 入門

Flutter GPU 是 Flutter 內建的低階圖形 API。它允許您透過撰寫 Dart 程式碼和 GLSL 著色器在 Flutter 中建立和整合自訂渲染器。不需要原生平台程式碼。

目前,Flutter GPU 處於早期預覽階段,並提供基本的柵格化 API,但隨著 API 逐漸穩定,將會繼續新增和改進更多功能。

Flutter GPU 還需要 啟用 Impeller。這表示它只能在 Impeller 支援的平台上使用。在撰寫本文時,Impeller 支援:

  • iOS(預設啟用)
  • macOS(選擇性預覽)
  • Android(選擇性預覽)

我們對 Flutter GPU 的目標是最終支援所有 Flutter 的平台目標。最終目標是促進 Flutter 中跨平台渲染解決方案的生態系統,這些解決方案對於套件作者來說易於維護,對於使用者來說易於安裝。

3D 渲染只是一個可能的用例。Flutter GPU 也可以用於建立專用的 2D 渲染器,或者執行更非正統的操作,例如渲染 4D 空間的 3D 切片,或投影非歐幾里德空間。

由 Flutter GPU 支援的自訂 2D 渲染器的絕佳用例範例將是依賴骨骼網格變形的 2D 角色動畫格式。Spine 2D 就是一個很好的例子。這種骨骼網格解決方案通常具有動畫剪輯,這些剪輯會操縱層次結構中骨骼的平移、旋轉和縮放屬性,並且每個頂點都具有一些關聯的「骨骼權重」,這些權重決定哪些骨骼應該影響頂點,以及影響程度。

使用像 drawVertices 這樣的 Canvas 解決方案,需要在 CPU 上對每個頂點應用骨骼權重轉換。使用 Flutter GPU,骨骼轉換可以以統一陣列或甚至紋理取樣器的形式傳遞到頂點著色器,允許根據骨骼狀態和每個頂點的骨骼權重在 GPU 上並行計算每個頂點的最終位置。

說到此,讓我們透過一個溫和的介紹來開始使用 Flutter GPU:繪製您的第一個三角形!

將 Flutter GPU 加入您的專案

首先,請注意 Flutter GPU 目前處於早期預覽狀態,可能會發生 API 斷裂。目前 API 已經可以實現很多功能,但是有經驗的圖形工程師可能會注意到一些缺失的常見功能。Flutter GPU 在接下來的幾個月裡將會新增許多功能。

基於這些原因,強烈建議您在針對 Flutter GPU 開發套件時,暫時使用 主頻道 的頂端。如果您遇到任何意外行為、錯誤或有功能請求,請使用標準的 Flutter 問題範本 在 GitHub 上提交問題。與 Flutter GPU 相關的所有追蹤問題都標記為 flutter-gpu 標籤

因此,在試驗 Flutter GPU 之前,請透過執行以下命令將 Flutter 切換到 main channel。

1
2
flutter channel main
flutter upgrade

現在建立一個新的 Flutter 專案。

1
2
flutter create my_cool_renderer
cd my_cool_renderer

接下來,將 flutter_gpu SDK 套件加入到您的 pubspec 中。

1
flutter pub add flutter_gpu --sdk=flutter

建立和匯入著色器捆綁包。

為了使用 Flutter GPU 渲染任何東西,您需要撰寫一些 GLSL 著色器。Flutter GPU 的著色器與 Flutter 的 片段著色器 功能所使用的著色器具有不同的語義,特別是在統一繫結方面。您還需要定義一個頂點著色器,與片段著色器一起使用。

從定義最簡單的著色器開始。您可以將著色器放置在專案中的任何位置,但是對於此範例,請建立一個 shaders 目錄,並使用兩個著色器填充它:simple.vert 和 simple.frag。

1
2
3
4
5
6
7
// 複製到:shaders/simple.vert

in vec2 position;

void main() {
gl_Position = vec4(position, 0.0, 1.0);
}

在繪製三角形時,您將擁有一個定義每個頂點的資料列表。在本例中,它僅列出 2D 位置。對於這些頂點中的每一個,簡單的頂點著色器都會將這些 2D 位置分配給剪輯空間輸出內在 gl_Position。

1
2
3
4
5
6
7
// 複製到:shaders/simple.frag

out vec4 frag_color;

void main() {
frag_color = vec4(0, 1, 0, 1);
}

片段著色器甚至更簡單;它輸出一個 RGBA 顏色,範圍為 (0, 0, 0, 0) 到 (1, 1, 1, 1)。因此,所有內容都將被著色為綠色。

好的,現在您有了著色器,請使用 Flutter 的提前編譯 (AOT) 著色器編譯器將它們編譯。為了為著色器捆綁包設定自動化的建置,我們建議您使用 flutter_gpu_shaders 套件。

使用 pub 將 flutter_gpu_shaders 作為專案中的常規相依性加入。

1
flutter pub add flutter_gpu_shaders

Flutter GPU 著色器捆綁到 .shaderbundle 檔案中,可以作為常規資產添加到專案的資產捆綁包中。著色器捆綁包包含針對平台目標的編譯著色器來源。

接下來,建立一個著色器捆綁包宣告檔案,描述著色器捆綁包的內容。將以下內容添加到專案根目錄中的 my_renderer.shaderbundle.json。

1
2
3
4
5
6
7
8
9
10
{
"SimpleVertex": {
"type": "vertex",
"file": "shaders/simple.vert"
},
"SimpleFragment": {
"type": "fragment",
"file": "shaders/simple.frag"
}
}

著色器捆綁包中的每個條目都可以具有任意名稱。在本例中,名稱是「SimpleVertex」和「SimpleFragment」。這些名稱用於在您的應用程式中查找著色器。

接下來,使用 flutter_gpu_shaders 套件建置 shaderbundle。您可以透過啟用實驗性的「原生資產」功能來新增一個掛鉤,該掛鉤會自動觸發建置。使用以下命令來啟用原生資產並安裝 native_assets_cli 套件。

1
2
flutter config --enable-native-assets
flutter pub add native_assets_cli

啟用原生資產功能後,在掛鉤目錄下新增一個 build.dart 脚本,它將自動觸發建置著色器捆綁包。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 複製到:hook/build.dart

import 'package:native_assets_cli/native_assets_cli.dart';
import 'package:flutter_gpu_shaders/build.dart';

void main(List<String> args) async {
await build(args, (config, output) async {
await buildShaderBundleJson(
buildConfig: config,
buildOutput: output,
manifestFileName: 'my_renderer.shaderbundle.json');
});
}

進行此更改後,當 Flutter 工具建置專案時,buildShaderBundleJson 將建置著色器捆綁包,並將結果輸出到套件根目錄下的 build/shaderbundles/my_renderer.shaderbundle。

著色器捆綁包格式本身與您使用的 Flutter 特定版本綁定,並且可能會隨著時間推移而改變。如果您正在撰寫一個建置著色器捆綁包的套件,請不要將生成的 .shaderbundle 檔案檢查到您的原始碼樹中。相反,請使用建置掛鉤來自動化建置流程(如前所述)。

這樣一來,使用您函式庫的開發人員將始終使用正確格式建置新的著色器捆綁包!

現在,您已經自動化了著色器捆綁包的建置,請像常規資產一樣匯入它。將資產條目添加到專案的 pubspec.yaml 中:

1
2
3
flutter:
assets:
- build/shaderbundles/

在未來,原生資產功能將允許建置掛鉤將資料資產附加到捆綁包中。一旦發生這種情況,就不需要再在建置掛鉤旁邊新增資 * * * * *產匯入規則了。

接下來,新增一些程式碼,在執行時載入著色器。建立 lib/shaders.dart 並新增以下程式碼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 複製到:lib/shaders.dart

import 'package:flutter_gpu/gpu.dart' as gpu;

const String _kShaderBundlePath =
'build/shaderbundles/my_renderer.shaderbundle';
// 注意:如果您正在建立函式庫,則路徑必須以套件名稱為前綴
// 例如:
// 'packages/my_cool_renderer/build/shaderbundles/my_renderer.shaderbundle'

gpu.ShaderLibrary? _shaderLibrary;
gpu.ShaderLibrary get shaderLibrary {
if (_shaderLibrary != null) {
return _shaderLibrary!;
}
_shaderLibrary = gpu.ShaderLibrary.fromAsset(_kShaderBundlePath);
if (_shaderLibrary != null) {
return _shaderLibrary!;
}

throw Exception("Failed to load shader bundle! ($_kShaderBundlePath)");
}

此程式碼為 Flutter GPU 著色器執行時函式庫建立一個單例 getter。第一次訪問 shaderLibrary 時,會使用 gpu.ShaderLibrary.fromAsset(shader_bundle_path) 使用建置的資產捆綁包初始化執行時著色器函式庫。

專案現在已經設定好使用 Flutter GPU 著色器。是時候渲染那個三角形了!

绘制您的第一個三角形

對於本指南,您將建立一個 RGBA Flutter GPU 紋理和一個 RenderPass,將紋理作為顏色輸出附加到它。然後,您將使用 Canvas.drawImage 在 Widget 中渲染紋理。

為了簡潔起見,您將放棄最佳實務,只會為每一幀重新建置所有資源。

只要您在分配紋理時將其標記為「著色器可讀取」,您就可以將其轉換為 dart:ui.Image。若要將渲染結果顯示在 Widget 樹中,請將其繪製到 dart:ui.Canvas 上!

您可以透過使用自訂畫家為 Widget 樹架設腳手架來存取 Canvas。將 lib/main.dart 的內容替換為以下內容:

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
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:flutter_gpu/gpu.dart' as gpu;

// 注意:我們之前在設定著色器捆綁包匯入時建立了它!
import 'shaders.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter GPU Triangle Example',
home: CustomPaint(
painter: TrianglePainter(),
),
);
}
}

class TrianglePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// 嘗試存取 `gpu.gpuContext`。
// 如果 Flutter GPU 不受支援,將會拋出異常。
print('Default color format: ' +
gpu.gpuContext.defaultColorFormat.toString());
}

@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

現在,執行應用程式。提醒一下,Flutter GPU 目前需要 啟用 Impeller。因此,您必須使用 Impeller 支援的平台。對於本指南,我將以 macOS 為目標。

1
flutter run -d macos --enable-impeller

如果 Flutter GPU 正常工作,那麼您應該會看到預設顏色格式被列印到主控台中。

1
flutter: Default color format: PixelFormat.b8g8r8a8UNormInt

如果 Impeller 未啟用,則在嘗試存取 gpu.gpuContext 時會拋出異常。

1
2
3
4
Exception: Flutter GPU requires the Impeller rendering backend to be enabled.

The relevant error-causing widget was:
CustomPaint

為了簡便起見,您只會從這裡開始修改 paint 方法。

首先,建立一個 Flutter GPU 紋理,清除它,然後透過將其繪製到 Canvas 上來顯示它。

建立一個與 Canvas 大小相同的紋理。必須選擇一個 StorageMode。在本例中,您將紋理標記為 devicePrivate,因為您將只使用從設備 (GPU) 存取紋理記憶體的指令。

1
2
final texture = gpu.gpuContext.createTexture(gpu.StorageMode.devicePrivate,
size.width.toInt(), size.height.toInt())!;

如果透過從主機 (CPU) 上傳資料來覆蓋紋理的資料,則使用 StorageMode.hostVisible

第三個可用的選項是 StorageMode.deviceTransient,它對於不需要超過單個 RenderPass 壽命的附件很有用(因此它們可以只存在於瓦片記憶體中,並且不需要由 VRAM 分配支援)。通常,深度/模板紋理符合此標準。

接下來,定義一個 RenderTarget。渲染目標包含一組「附件」,描述每個片段的記憶體佈局及其在 RenderPass 開始和結束時的設定/拆卸行為。

本質上,RenderTarget 是 RenderPass 的可重複使用描述器。

現在,定義一個非常簡單的 RenderTarget,它只包含一個顏色附件。

1
2
final renderTarget = gpu.RenderTarget.singleColor(
gpu.ColorAttachment(texture: texture, clearValue: Colors.lightBlue));

請注意,此程式碼將 clearValue 設定為淡藍色。每個附件都具有 LoadActionStoreAction,它們分別確定在傳遞的開始和結束時應該對附件的臨時瓦片記憶體執行什麼操作。

預設情況下,顏色附件被設定為 LoadAction.clear(它將瓦片記憶體初始化為給定的顏色)和 StoreAction.store(它將結果儲存到附加的紋理的 VRAM 分配中)。

現在,建立一個 CommandBuffer,使用前面的 RenderTarget 從其中產生一個 RenderPass,然後立即提交 CommandBuffer 以清除紋理。

1
2
3
4
final commandBuffer = gpu.gpuContext.createCommandBuffer();
final renderPass = commandBuffer.createRenderPass(renderTarget);
// ... 繪製調用將放在這裡!
commandBuffer.submit();

剩下的就是將初始化的紋理繪製到 Canvas 上!

1
2
final image = texture.asImage();
canvas.drawImage(image, Offset.zero, Paint());

現在您有了連接到螢幕顯示結果的 RenderPass,您就可以開始繪製三角形了。若要執行此操作,請設定以下內容:

  1. 從著色器建立的 RenderPipeline,以及
  2. 包含幾何形狀的 GPU 可存取緩衝區(三個頂點位置)。

建立 RenderPipeline 很容易。您只需將函式庫中的頂點和片段著色器組合在一起。

1
2
3
final vert = shaderLibrary['SimpleVertex']!;
final frag = shaderLibrary['SimpleFragment']!;
final pipeline = gpu.gpuContext.createRenderPipeline(vert, frag);

現在是幾何形狀。回想一下,「SimpleVertex」著色器只有一個輸入:in vec2 position。因此,若要繪製三個頂點,您需要三組兩個浮點數。

1
2
3
4
5
6
7
final vertices = Float32List.fromList([
-0.5, -0.5, // 第一個頂點
0.5, -0.5, // 第二個頂點
0.0, 0.5, // 第三個頂點
]);
final verticesDeviceBuffer = gpu.gpuContext
.createDeviceBufferWithCopy(ByteData.sublistView(vertices))!;

剩下的就是繫結新的資源,並呼叫 renderPass.draw() 以完成記錄繪製調用。

1
2
3
4
5
6
7
8
9
10
renderPass.bindPipeline(pipeline);

final verticesView = gpu.BufferView(
verticesDeviceBuffer,
offsetInBytes: 0,
lengthInBytes: verticesDeviceBuffer.sizeInBytes,
);
renderPass.bindVertexBuffer(verticesView, 3);

renderPass.draw();

如果您啟動應用程式,您現在應該會看到一個綠色的三角形!

太棒了,您使用 Flutter、Dart 和一點 GLSL 從頭開始建立了一個渲染器!

無論這是否是您第一次渲染三角形,或者您是經驗豐富的圖形專家,我都希望您能繼續使用 Flutter GPU,並查看我們正在開發的套件,例如 Flutter Scene。

在未來,我們希望發佈友好的初學者程式碼實驗室,深入探討 Flutter GPU 的預設行為和最佳實務。我們還沒討論頂點屬性佈局、紋理繫結、統一和對齊要求、管道混合、深度和模板附件、透視校正等等!

在那之前,我建議您探索 Flutter Scene 作為如何使用 Flutter GPU 的更全面的範例。

使用 Flutter Scene 進行 3D 渲染

Flutter Scene(套件 flutter_scene)是一個新的 3D 場景圖套件,由 Flutter GPU 支援,它使 Flutter 開發人員能夠匯入動畫 glTF 模型並渲染即時 3D 場景。

目的是提供一個套件,讓在 Flutter 中輕鬆建立互動式 3D 應用程式和遊戲。

該套件最初是作為一個 dart:ui 擴展,用於用 C++ 編寫的 3D 渲染器,並直接建置到 Flutter 的原生執行時中,但它已經使用更靈活的介面針對 Flutter GPU 重新撰寫。

與 Flutter GPU API 本身一樣,Flutter Scene 目前處於早期預覽狀態,需要 啟用 Impeller。Flutter Scene 通常與 Flutter GPU API 的重大變更保持同步,因此強烈建議您在試驗 Flutter Scene 時使用 主頻道

接下來,使用 Flutter Scene 建立一個應用程式!

設定 Flutter Scene 專案

由於強烈建議您針對 主頻道 使用 Flutter Scene,請從切換到主頻道開始。

1
2
flutter channel main
flutter upgrade

接下來,建立一個新的 Flutter 專案。

1
2
flutter create my_3d_app
cd my_3d_app

Flutter Scene 依靠實驗性的「原生資產」功能來自動化著色器的建置。您將在稍後使用原生資產來設定自動匯入 Flutter Scene 的 3D 模型。

使用以下命令啟用原生資產。

1
flutter config --enable-native-assets

最後,將 Flutter Scene 添加為專案相依性。

您還需要在與 Flutter Scene 的 API 互動時使用一些 vector_math 構造,因此也添加 vector_math 套件。

1
flutter pub add flutter_scene vector_math

接下來,匯入一個 3D 模型!

匯入 3D 模型

首先,您需要一個要渲染的 3D 模型。對於本指南,您將使用常見的 glTF 樣本資產: DamagedHelmet.glb。以下是它的樣子。

原始的 Damaged Helmet 模型由 theblueturtle_ 在 2016 年建立(授權: [CC BY-NC 4.0 國際](https://creativecommons.org/licenses/by-nc/4.0/legalcode))。轉換後的 glTF 版本由 ctxwing 在 2018 年建立(授權: [CC BY 4.0 國際](https://creativecommons.org/licenses/by/4.0/legalcode))。

您可以從 GitHub 上託管的 glTF 樣本資產儲存庫 中獲取它。將 DamagedHelmet.glb 放置在您的專案根目錄中。

1
curl -O https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/main/2.0/DamagedHelmet/glTF-Binary/DamagedHelmet.glb

像大多數即時 3D 渲染器一樣,Flutter Scene 在內部使用專用的 3D 模型格式。您可以使用 Flutter Scene 的離線匯入器工具將標準 glTF 二進制檔案(.glb 檔案)轉換為此格式。

將 flutter_scene_importer 套件作為常規相依性添加到專案中。

1
flutter pub add flutter_scene_importer

添加此套件可以使用 dart run 手動呼叫匯入器。

1
2
3
4
dart --enable-experiment=native-assets \
run flutter_scene_importer:import \
--input "path/to/my/source_model.glb" \
--output "path/to/my/imported_model.model"

您可以透過使用原生資產建置掛鉤來自動執行匯入器。若要執行此操作,請先將 native_assets_cli 作為常規專案相依性安裝。

1
flutter pub add native_assets_cli

現在您可以撰寫建置掛鉤了。使用以下內容建立 hook/build.dart。

1
2
3
4
5
6
7
8
9
10
import 'package:native_assets_cli/native_assets_cli.dart';
import 'package:flutter_scene_importer/build_hooks.dart';

void main(List<String> args) {
build(args, (config, output) async {
buildModels(buildConfig: config, inputFilePaths: [
'DamagedHelmet.glb',
]);
});
}

使用 flutter_scene_importer 中的 buildModels 公用程式,提供要建置的模型列表。路徑相對於專案的建置根目錄。

當 Flutter 工具建置專案時,buildModels 現在將建置著色器捆綁包,並將結果輸出到套件根目錄下的 build/models/DamagedModel.model。

匯入的模型格式本身與您使用的 Flutter Scene 特定版本綁定,並且會隨著時間推移而改變。在撰寫使用 Flutter Scene 的應用程式或函式庫時,請勿將生成的 .model 檔案檢查到您的原始碼樹中。相反,請使用建置掛鉤從您的原始模型中生成它們(如前所述)。

這樣一來,隨著時間推移升級 Flutter Scene 時,您將始終使用正確格式建置新的 .model 檔案!

接下來,像常規資產一樣匯入模型。將資產條目添加到專案的 pubspec.yaml 中。

1
2
3
flutter:
assets:
- build/models/

在未來,原生資產功能將允許建置掛鉤將資料資產附加到捆綁包中。一旦發生這種情況,就不需要再在建置掛鉤旁邊新增資產匯入規則了。

渲染 3D 場景

現在是應用程式的程式碼了。

首先,建立一個有狀態的 Widget,用於在多個幀中保留 Scene。

您將根據時間進行動畫處理,因此將 SingleTickerProviderStateMixin 添加到狀態,以及一個 elapsedSeconds 成員。

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
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_scene/camera.dart';
import 'package:flutter_scene/node.dart';
import 'package:flutter_scene/scene.dart';
import 'package:vector_math/vector_math.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatefulWidget{
const MyApp({super.key});

@override
MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
double elapsedSeconds = 0;
Scene scene = Scene();

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My 3D app',
home: Placeholder(),
);
}
}

執行應用程式作為冒煙測試,以確保沒有錯誤。請記住要 啟用 Impeller

1
flutter run -d macos --enable-impeller

在繼續之前,請為動畫設定計時器。覆蓋 MyAppState 中的 initState 以呼叫 createTicker。

1
2
3
4
5
6
7
8
9
10
@override
void initState() {
createTicker((elapsed) {
setState(() {
elapsedSeconds = elapsed.inMilliseconds.toDouble() / 1000;
});
}).start();

super.initState();
}

只要 Widget 可見,計時器回呼就會為每一幀被呼叫。呼叫 setState 會觸發此 Widget 在每一幀重建。

接下來,載入之前放置在專案中的 3D 模型,並將其添加到 Scene 中。

使用 Node.fromAsset 從資產捆綁包中載入模型。將以下程式碼放置在 initState 中。

1
2
3
4
Node.fromAsset('build/models/DamagedHelmet.model').then((model) {
model.name = 'Helmet';
scene.add(model);
});

Node.fromAsset 會異步地從資產捆綁包中反序列化模型,並在模型準備好添加到場景中時解析返回的 Future

現在,MyAppState.initState 應該如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@override
void initState() {
createTicker((elapsed) {
setState(() {
elapsedSeconds = elapsed.inMilliseconds.toDouble() / 1000;
});
}).start();

Node.fromAsset('build/models/DamagedHelmet.model').then((model) {
model.name = 'Helmet';
scene.add(model);
});

super.initState();
}

但是,您仍然沒有實際渲染 3D Scene!若要執行此操作,請使用 Scene.render,它需要 UI Canvas、Flutter Scene Camera 和一個大小。

存取 Canvas 的一種方法是建立一個 CustomPainter:

1
2
3
4
5
6
7
8
9
10
11
12
13
class ScenePainter extends CustomPainter {
ScenePainter({required this.scene, required this.camera});
Scene scene;
Camera camera;

@override
void paint(Canvas canvas, Size size) {
scene.render(camera, canvas, viewport: Offset.zero & size);
}

@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

不要忘記將 shouldRepaint 覆蓋設置為返回 true,以便在每次重建發生時自訂畫家都會重新繪畫。

最後,將 CustomPainter 添加到源樹中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@override
Widget build(BuildContext context) {
final painter = ScenePainter(
scene: scene,
camera: PerspectiveCamera(
position: Vector3(sin(elapsedSeconds) * 3, 2, cos(elapsedSeconds) * 3),
target: Vector3(0, 0, 0),
),
);

return MaterialApp(
title: 'My 3D app',
home: CustomPaint(painter: painter),
);
}

此程式碼指示相機沿著一個連續的圓圈移動,但始終面向原點。

最後,啟動應用程式!

1
flutter run -d macos --enable-impeller

以下是我們組合的完整源程式碼。

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_scene/camera.dart';
import 'package:flutter_scene/node.dart';
import 'package:flutter_scene/scene.dart';
import 'package:vector_math/vector_math.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatefulWidget {
const MyApp({super.key});

@override
MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
double elapsedSeconds = 0;
Scene scene = Scene();

@override
void initState() {
createTicker((elapsed) {
setState(() {
elapsedSeconds = elapsed.inMilliseconds.toDouble() / 1000;
});
}).start();

Node.fromAsset('build/models/DamagedHelmet.model').then((model) {
model.name = 'Helmet';
scene.add(model);
});

super.initState();
}

@override
Widget build(BuildContext context) {
final painter = ScenePainter(
scene: scene,
camera: PerspectiveCamera(
position: Vector3(sin(elapsedSeconds) * 3, 2, cos(elapsedSeconds) * 3),
target: Vector3(0, 0, 0),
),
);

return MaterialApp(
title: 'My 3D app',
home: CustomPaint(painter: painter),
);
}
}

class ScenePainter extends CustomPainter {
ScenePainter({required this.scene, required this.camera});
Scene scene;
Camera camera;

@override
void paint(Canvas canvas, Size size) {
scene.render(camera, canvas, viewport: Offset.zero & size);
}

@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

Flutter 光明的未來

如果您能夠成功地遵循這些指南中的其中一個並讓它運行起來:太棒了,恭喜!

Flutter GPU 和 Flutter Scene 都非常年輕,平台支援有限。但我想總有一天,我們會懷念這些不起眼的開端。

隨著 Impeller 的推廣,Flutter 團隊完全掌控了渲染堆疊,因為我们需要針對 Flutter 的用例專門化渲染器。現在,我們正在開啟 Flutter 歷史上的一個新篇章。一個由您共同掌控渲染的篇章!

Flutter Scene 最初是 Impeller 中的 C++ 組件,與 2D Canvas 渲染器一起,帶有一個精簡的 dart:ui 擴展。在我構建它時,我已經意識到 Flutter Engine 不會是它的最終目的地。

3D 渲染器的架構決策海洋是廣闊