【文章翻譯】What are the important & difficult tasks for Flutter devs? — Q1 2020 survey results

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

Flutter 開發人員的哪些任務重要且困難? - 2020 年第一季調查結果

作者:JaYoung LeeYouyang Hou

我們每季都會對 Flutter 開發人員進行調查,以更好地了解您如何使用 Flutter 以及我們可以改進哪些方面。今天,我們將發佈第八次季度調查的結果,該調查於 2020 年 2 月進行。我們在 8 天內收到了大約 6,000 份回覆,很高兴地得知,有史以來最高比例的開發人員(94.5%)表示對 Flutter 感到滿意。雖然我們每季都會問一些問題以了解長期趨勢,但本季度的調查特別側重於識別對使用者而言重要且困難的關鍵任務。在深入探討結果之前,以下是一些其他的有趣發現。

關於 Flutter 使用者

首先,我們了解到,與上個季度相比,使用者對 Flutter 的核心框架 (+2%) 和生態系統 (+4%) 更為滿意。但使用者對動畫框架的滿意度略有下降 (-3%)。為了進一步了解造成這種情況的原因,我們将在下一次調查中詢問您關於動畫框架體驗的更多問題。同時,請查看我們 YouTube 頻道上的「在 Flutter 中製作動畫」播放清單。所有這些影片都是最近發佈的。

接下來,大約一半的受訪者表示他們已經使用 Flutter 發佈了應用程式 (51%)。這個數字一直在持續增長 - 大約一年前,這個數字是 34%。那些發佈了應用程式的開發人員對 Flutter 的滿意度比那些未發佈應用程式的開發人員高。

應用程式開發階段的 Flutter 滿意度。發佈了 Flutter 應用的開發人員對 Flutter 的滿意度比那些未發佈應用的開發人員高。

同樣,當我們詢問他們使用 Flutter 的主要目的時,48% 的受訪者表示他們正在使用 Flutter 進行主要工作。這個數字也在增長。一年前,這個數字是 30%。評估 Flutter 以便潛在生產使用的使用者數量一直在下降,從 2019 年的 36% 降至 2020 年的 25%。這意味著許多使用者已經完成了評估,並將 Flutter 用於更嚴肅的目的。

最後,我們發現,69% 發佈了完整生產應用的開發人員正在構建 iOS 和 Android 應用程式。16% 的開發人員表示,除了兩個行動平台之外,他們還嘗試了 Web 平台。目前,Play 商店中大約有 50,000 個 Flutter 應用程式。這個數字一直在呈指數級增長,我們希望看到這種趨勢在未來幾個月內繼續下去。

更多統計數據、見解和未來計畫,請參閱「Flutter 2020 年春季更新」文章。

關於任務

部分調查受訪者被邀請參加更長的調查。因此,43% 的受訪者(N = 2,598)回答了一組問題,我們在這些問題中詢問了他們對 29 個 Flutter 相關任務的看法。這些任務涵蓋了 UI 開發、框架和工具、生態系統、效能和疑難排解以及測試和優化。這些領域的所有 29 個任務都被認為對 Flutter 使用者至關重要。然而,我們假設其中一些任務比其他任務更重要且更難,因此需要團隊更多關注。

我們以這樣的方式設計調查:隨機選取一半的受訪者詢問他們認為哪些任務對他們很重要,另一半的受訪者詢問他們認為哪些任務很難完成。透過將受訪者分成兩組(也稱為「組間設計」),我們能夠減少向每個人提出的問題總數。此外,透過要求使用者評估任務的重要性程度和難度,我們能夠識別出對 Flutter 開發人員既重要又困難的最高優先級任務。以下是團隊用於決定如何處理每個任務的象限圖。

重要程度-難度象限支援決策。

以下任務被評為排名前 6 的最重大的任務。值得注意的是,這些重要的任務廣泛分佈在應用程式開發的不同階段,從效能和測試到 UI 開發。

  • 在我的 Flutter 應用程式中除錯錯誤和崩潰
  • 測試我的應用程式,以確保它可以在各種平台上正常運作
  • 為我的 Flutter 應用程式選擇一種狀態管理方法
  • 了解和解決佈局問題(例如文字溢出)
  • 根據 UX UI 設計師提供的設計規範建立 UI
  • 為我的 Flutter 應用程式排除平台特定問題
任務的相對重要性。藍點表示個人的估計重要性分數,紅點表示平均值。

以下任務被評為排名前 6 的最困難的任務。我們發現,與疑難排解和效能相關的幾個任務對於 Flutter 專業開發人員來說很困難。大多數與 UI 相關的任務被評為不太困難。

  • 為我的 Flutter 應用程式排除平台特定問題
  • 診斷和修復我的 Flutter 應用程式的記憶體問題
  • 診斷和修復我的 Flutter 應用程式的 CPU 使用率問題
  • 將現有的平台特定原生 API 加入我的 Flutter 應用程式
  • 診斷和修復我的 Flutter 應用程式的 UI 卡頓
  • 開發一個 Flutter Plugin(帶有平台特定實作)
任務的相對難度。藍點表示個人的估計重要性分數,紅點表示平均值。

如您所見,一些任務同時出現在高度重要和困難的任務列表中(例如「為我的 Flutter 應用程式排除平台特定問題」)。這些知識對 Flutter 團隊很有價值,因為他們可以根據使用者輸入優先考慮這些領域,而不是憑藉猜測。

此外,由於 Flutter 在企業客戶中快速普及(26% 的調查受訪者在企業工作),我們進行了額外的分析,以找出大型公司開發人員特有的挑戰。對於這組開發人員,我們發現,減少應用程式大小、將 Flutter 加入現有的應用程式(即附加到應用程式)、選擇狀態管理技術以及了解和解決佈局問題,對於他們來說比其他使用者更重要也更困難。

Flutter 團隊現在更加了解您認為既重要又困難的任務,並將使用這些輸入來指導開發重點。此外,團隊將會持續追蹤改進,並重新評估任務,以確保每個開發步驟都健康且得到良好支援。

接下來呢?

在 Flutter 這樣的開源專案中工作的一大樂趣就是擁有一個願意提供見解並提出改進建議(無論是在調查中還是透過 GitHub 問題等其他形式的回饋)的社群。因此,我們要特別感謝您填寫調查問卷。我們中的幾個人閱讀了所有提交的評論。

我們希望這些報告能證明我們對了解您的回饋非常感興趣。在下一份季度調查中,我們計畫針對本次調查中的一些見解進行跟進,特別是我們希望更好地了解上面強調的重要且困難的任務。請留意將很快發佈的下一份調查。

Flutter 的 UX 研究團隊也正在進行各種使用者體驗研究,以使您使用 Flutter 的體驗更加愉快。如果您有興趣參與未來的研究,請註冊


Flutter 開發人員的哪些任務重要且困难? - 2020 年第一季調查結果 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

【文章翻譯】Announcing Adobe XD support for Flutter

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

在 XD 中創作,並匯出到可運行的 Flutter 程式碼

我們的 Flutter 目標是提供一個豐富的畫布,用於創意表達。在 iOS 和 Android 上擁有原生效能,對螢幕上渲染的每個像素擁有完全控制權,並且能夠以有狀態的熱重載快速迭代,我們希望釋放設計師和開發人員的潛力,構建不受人工技術界限限制的美麗體驗。

去年在我們的 Flutter Interact 活動 中,我們將焦點轉向了正在使用 Flutter 進行探索和實驗的創作者。我們聽取了像 Robert Felker 這樣的數位藝術家的意見,他使用 Flutter 透過生成演算法來構建虛幻形式。我們展示了 gskinner 等創意代理機構的作品,他們創作了一系列創新的片段,以展示 Flutter 的潛力。並且我們看到了 Adobe 對 Adobe XD 的 Plugin 初期原型 的演示,該原型能將 Flutter 程式碼直接從他們的工具中匯出。

Flutter 是一個豐富的創意表達畫布:一個用於構建不受傳統技術界限限制的美麗原生體驗的工具包。(使用 Flutter 構建的生成藝術品,由 Robert Felker 提供。)

今天,我們很高興與 Adobe 共同宣布,他們將 XD 轉換為 Flutter 的 Plugin 現已 作為搶先體驗版 提供,供更廣泛的公眾測試。 Adobe XD 是一個 UI/UX 設計和協作工具,幫助團隊為網站、應用程式、語音介面、遊戲等等建立和分享設計。作為 Adobe 領先的 Creative Cloud 的一部分,XD 允許設計師將向量藝術、文字、圖片、微互動和動畫轉化為互動式原型,這些原型就像運作中的軟體產品一樣。將設計匯出到 Flutter 的功能進一步縮短了創意想法和產品開發之間的延遲,因為 XD 原型現在可以在幾分鐘內變成可運行的 Flutter 程式碼。Adobe XD 支援在 Windows 或 macOS 上進行設計,並且包括 免費入門計劃,讓您可以快速上手。

將 Adobe XD 匯出到 Flutter 現在只是一個 Plugin 的距離。

從 Adobe XD 匯出 Flutter 程式碼

在 XD 中使用 Flutter Plugin 很簡單。您可以匯出單個繪圖或組件,也可以匯出畫板。以下是操作方法。

首先安裝 Flutter 匯出 Plugin。從 Adobe XD 中,選擇 Plugins > Discover Plugins,然後搜尋 Flutter。安裝完畢後,您可以透過選擇 Plugins > Flutter > UI Panel 來顯示上面螢幕截圖中顯示的 UI 面板。

現在,透過將其包含在您的 pubspec.yaml 應用程式清單中,將 adobe_xd 套件加入到您的 Flutter 專案中。此套件提供輔助函數,可在生成的 XD 程式碼中最大限度地減少樣板。

若要匯出單個元素,只需選取您要匯出到 Flutter 的單個 Widget,然後從 UI 面板中選擇 Copy Selected 按鈕。這會將相關的 Dart 程式碼複製到您的剪貼簿中,您可以將其作為無狀態或有狀態 Widget 的基礎:

匯出的程式碼可以整合到您現有的專案中,並且可以在不重新處理其他檔案的情況下更新。

另一種方法是匯出整個專案。假設您已經有一個要載入內容的 Flutter 應用程式(包括 pubspec.yaml 中的 adobe_xd 套件參考),您只需從 UI 面板中選擇 Plugins > Flutter > Export All Widgets,並設定您想要的任何其他配置選項。

這會在您的專案的 lib/ 子目錄中建立一系列類別,然後您可以直接使用這些類別。您可以繼續調整 XD 原型並使用 ⇧⌘F(Windows 上為 Ctrl+Shift+F)再次匯出,如果您已在 Visual Studio Code 中啟用 Dart Hot Reload on Save Watcher 設定,您的應用程式會在您重新匯出 Widget 時自動重新載入任何更新。

從 XD 到程式碼的快速週期為您提供了一種將原型轉化為應用程式的新方法。

作為搶先體驗版,當然有一些限制,這些限制在 發行備註 中有說明。一個顯著的限制是響應式佈局尚不可用,等待新的 XD API 完成。但是,當像這樣的功能可用時,您會自動獲得 Plugin 的更新。

與 Adobe 合作開發這個 Plugin 真是太好了。我們很興奮能得到他們的支援,而 新的基於 JavaScript 的 API 使得每個人都能輕鬆擴展 Adobe XD。以下是一些關於這個新 Plugin 的觀點:

“在 Adobe,我們一直在尋找簡化設計師到開發人員工作流程的方法,這種工作流程困擾著許多設計和構建應用程式的團隊。今天,我們很興奮地發佈與 Flutter 合作開發的成果的搶先體驗版,該成果旨在消除猜測,加快決策速度,並幫助團隊更快地將新體驗推向市場。”
- Vijay Vachani,Adobe Creative Cloud 平台和生態系統高級總監

若要了解更多資訊,請訪問 Adobe 的 XD 至 Flutter Plugin 頁面。我們期待看到您使用它創作的成果!


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

【文章翻譯】Announcing Flutter 1.17

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

宣布 Flutter 1.17

包含金屬支援以提升 iOS 效能、新的 Material 元件、新的網路追蹤工具等等!

由 Chris Sells 發佈,Flutter 開發者體驗產品經理

今天,我們很榮幸宣布 Flutter 1.17,這是我們 2020 年的第一個穩定版本。

今年對每個人來說都是充滿挑戰的一年。我們的目標是每季度發佈一次穩定版本;然而,這個版本花費的時間長了一些,因為我們一直在 重新設計我們的基礎架構以建立新的發佈流程。我們將品質視為我們的首要任務,並相信新的發佈模式將提高我們為穩定分支提供高品質熱修復的能力。此版本包含大量修復,自我們發佈 1.12 穩定版本以來,共修復了 6,339 個問題。此增長的一大部分歸功於 我們與 Nevercode 的合作,這讓我們在回應客戶問題時變得更靈活。今年到目前為止,我們修復的錯誤比新開的錯誤還多,導致問題總數減少了約 800 個。其中許多錯誤都是透過我們從 231 位貢獻者合併的 3,164 個 PR 來修復的。這些數字非常龐大,我們衷心感謝每位在這個充滿挑戰的時期為 Flutter 做出的努力和持續貢獻。

如果您有興趣查看我們在此版本中合併的所有 PR,您可以在 flutter.dev 網站 上查看它們。除了品質提升外,我們還成功地將一些新功能加入到這個版本中,包括 iOS 上的金屬支援、新的 Material 元件、新的網路追蹤工具等等!此版本還包含 Dart 2.8,您可以在 Dart 部落格 上閱讀更多相關資訊。

行動效能和尺寸提升

此版本的重點是持續在效能和記憶體提升方面進行整體工作。只要將您的應用程式升級到這個版本,您的使用者應該就能看到更快的動畫、更小的應用程式和更低的記憶體使用率。在此版本中,您將看到 預設導航情況(不透明路線,沒有透明度)的速度提升了 20% 到 37%。您還將看到簡單 iOS 動畫的 CPU/GPU 使用率降低了高達 40%,具體取決於硬體(詳細資訊請參閱 PR 14104PR 13976)。

此版本還透過 若干 修復 帶來 大幅 提升 了應用程式的大小。例如,針對 Android 的 Flutter Gallery 範例在 2019 年底為 9.6MB,現在為 8.1MB,減少了 18.5%。

在記憶體使用方面,此版本為快速捲軸大型圖片 帶來了 70% 的記憶體減少,這可能會導致效能提升,具體取決於設備的記憶體大小。

_測試應用程式記憶體使用量隨著時間推移合併 PR 的情況(較短的欄表示更好)_

然而,最廣泛的效能提升是 iOS 上金屬的支援。

金屬支援提高 iOS 效能 50%

Apple 對 iOS 上金屬的支援提供了幾乎直接存取底層 GPU,是 Apple 建議的圖形 API。在完全支援金屬的 iOS 設備上,Flutter 現在預設使用它,使得您的 Flutter 應用程式大部分時間都能更快運行, 平均渲染速度提升了約 50% (取決於您的工作負載)。

_測試 iOS 應用程式畫面渲染時間,OpenGL 對比金屬(較短的欄表示更好)_

對於不支持金屬的設備(A7 處理器之前的設備或運行 iOS 10 之前版本的設備),Flutter 將繼續像以前一樣使用 OpenGL,為舊設備提供原生渲染速度。如需了解更多詳細資訊,請查看 Flutter wiki 上的 iOS 金屬常見問題解答

Material 元件:NavigationRail、DatePicker 等等

基於內部和外部客戶的回饋,我們繼續發展和改進 Flutter 中實作的 Material 設計系統。在此版本中,我們添加了 NavigationRail,這是一個新的 Widget,它提供了靈活的應用程式導航模型。它是 由 Google Material Design 團隊設計 和實作的。NavigationRail 非常適合可以在行動和桌面外觀之間切換的應用程式,因為當您的應用程式螢幕尺寸增大時,它很容易替換為 BottomNavigator。

新的 NavigationRail Widget

若要查看 NavigationRail 的實際操作,請查看 web_dashboard 範例 或在 DartPad 上試用它。

除了新的 Widget 外,此版本還包含對 Material DatePicker 和文字選取溢出 Widget 的更新。

更新後的 Material DatePicker Widget

此 DatePicker 版本包含新的視覺效果,以匹配 更新後的 Material 指南,以及新的文字輸入模式。您可以在 Material 日期選取器重新設計 規格中閱讀更多詳細資訊。

在 Android 上更新的文字選取溢出
在 iOS 上更新的文字選取溢出

當按鈕的長度超過沒有溢出的顯示範圍時,文字選取選單現在在 AndroidiOS 上具有更好的保真度。這在選單項目文字可能很長的語言環境中尤其明顯。

在 Flutter 1.17 的同一時間範圍內,但透過非同步發佈方式,我們還發佈了 新的動畫套件,它提供了預先構建的動畫,這些動畫實作了 新的 Material 動畫規範

_動畫套件中 Container 變換的示例_

實作動畫部落格文章 中,Material Design 團隊定義了四種轉場模式,描述了組件和全螢幕視圖之間的動畫:Container 變換、共用軸線、淡入淡出,以及淡出。雖然這些動畫在 Flutter 中一直都是可能的,但動畫套件讓實作它們變得更加容易。將它們加入到您的應用程式中,讓您的使用者眼前一亮吧!

Material 文字比例:現代化 Flutter 文字主題

在此版本中,Flutter 團隊已完成對 2018 Material Design 規範中文字比例部分 的實作,同時不中斷現有的 Flutter 應用程式。在 PR 22330 中,我們於 2018 年 10 月添加了對新配置(但不是新名稱)的選用支援。現有的文字樣式名稱沒有更改,因為這樣做會產生重大 API 變更,可能會影響大多數應用程式。此版本更新了 TextTheme API 以匹配目前的 Material 規格,但保留了舊名稱,以確保您的程式碼不會中斷。然而,舊名稱已被棄用,因此您將收到警告以鼓勵您採用新名稱。

下表總結了 2018 Material TextStyles 的名稱和配置。

請注意,Material Design 規範中稱為 body1 和 body2 的 TextStyles 在 Flutter TextTheme API 中分別稱為 bodyText1 和 bodyText2。同樣,規範中稱為 H1-H6 的 TextStyles 在 TextTheme API 中分別稱為 headline1-headline6。

Google Fonts for Flutter

文字和字體是息息相關的,因此,如果您對新的 Material 文字比例實作感到興奮,那麼您可能也會對 新的 Google Fonts for Flutter v1.0 版本 感到興奮。

Google Fonts 非常便於在您的 Flutter 應用程式中使用

Google Fonts 讓開發人員能夠輕鬆地嘗試和在他們的應用程式中使用 fonts.google.com 上的任何字體。當應用程式準備好發佈時,開發人員可以決定使用者是透過從 API 下載字體來接收字體,還是將字體預先捆綁到應用程式包中。

無障礙功能和國際化

最後,我們持續關注的一個重要領域是無障礙功能,因為我們認為讓 Flutter 應用程式能被盡可能廣泛的受眾使用是一個優先事項。在此版本中,我們在各個方面都進行了工作,包括 捲軸文字 欄位其他輸入 Widget 的無障礙功能修復。您可以在 GitHub 上查看此版本中修復的所有無障礙功能問題。我們鼓勵您測試您自己的應用程式的無障礙功能,並且還更新了此版本的文檔,其中包含 一些建議的最佳實務

在國際化方面,我們一直在處理一些影響 Samsung 鍵盤 IME 的問題,這些問題會影響各種東亞語言的文字輸入。我們很高兴地宣布,我們已經完成了這項工作,特別是韓國開發人員應該會在這個版本中發現很多值得慶祝的事情

工具:將 Dart DevTools 移植到 Flutter、Android 快速啟動等等

此版本讓我們非常接近用新的 Flutter 版本替換目前的 Dart DevTools 版本。如果您想試一試,請啟動 DevTools,然後點擊 DevTools 右上角的「燒杯」圖示。

當您使用使用 Flutter 實作的 Dart DevTools 預發佈版本時,您可能會注意到各種改進,但最重大的改進是新的 網路 索引標籤。

如果您沒有在 Dart DevTools 的預發佈版本中看到網路索引標籤(例如,如果您從命令行運行它),您可以使用以下命令手動更新它:

1
$ pub global activate devtools

當您按下錄製按鈕後,網路索引標籤會顯示 Flutter 應用程式的網路流量。此外,如果您想從啟動應用程式的那一刻起就捕獲網路流量,您可以在 main() 方法中包含以下程式碼行:

1
2
3
4
5
void main() {
// 啟用網路流量記錄
HttpClient.enableTimelineLogging = true;
runApp(MyApp());
}

除了更新後的 Dart DevTools 外,此版本還加入了對 實驗性的「快速啟動」選項 的支援,讓您在目標為 Android 時,可以將 Flutter 應用程式除錯啟動速度提高多達 70%。您可以透過 flutter run --fast-start -d <您的 Android 設備> 來存取此選項。此選項會安裝一個通用的 Android 應用程式,它只依賴於您的 Plugin 程式碼,不包含任何 Dart 程式碼或資產。這允許重複的 flutter run 命令更快啟動,因為 Dart 程式碼或資產的更改不需要重新建立 APK。此選項會將您的應用程式捆綁到一個通用的 Android「封裝器」中,它實際上不會安裝到您的設備上,這與我們正常的啟動選項不同。此外,在某些情况下它無法正常工作,例如,當您使用存取背景執行的 Plugin 時。另一方面,如果您的 Android 除錯啟動時間很長,請嘗試一下。

如果您是 Android Studio 或 IntelliJ 使用者,您會發現熱重載功能更具彈性。在這個更改之前,如果您有任何分析錯誤,熱重載將不會重新載入您的程式碼。如果分析錯誤是對您目前正在運行的程式碼沒有影響的事情,例如在單元測試中,這會很令人沮喪。透過 這個更改,分析錯誤不足以阻止熱重載運作,而是根據 VM 的編譯錯誤來決定是否運作。

如果您想在 Android Studio 或 IntelliJ 的 Flutter Plugin 中更早存取此類更改,我們現在為 IntelliJ Plugin 提供了開發頻道,您可以選擇加入以獲得更快的更新速度。開發頻道的目標是讓 Flutter 開發人員在我們更廣泛地提供這些功能之前,對新的 IDE 整合功能提供回饋。如果您喜歡冒險,並且想為 Flutter 工具團隊提供早期回饋,請 立即註冊

對於 Visual Studio Code 使用者,我們推薦新的 Dart:列出過時套件 命令,它會運行 新的 pub outdated 命令

此命令旨在幫助您追踪相依項中的版本控制問題。

最後,如果您遇到 Flutter 崩潰,工具會提示您提交錯誤。

團隊會密切關注這些錯誤報告的嚴重程度和頻率,因此請在收到提示時提交它們。

客戶回饋:MGM 和 Superformula

當然,Flutter 的存在是为了实现美丽的应用体验。Superformula 是一个做大事的数字机构的例子,他们最近与 MGM Resorts 合作对他们的移动应用进行了重大更新,彻底用 Flutter 重建了它。他们报告称,“将 Flutter 添加到我们的核心产品中,为我们带来了更高的速度和灵活性,这对我们的客户及其用户来说都带来了真正可衡量的价值。”

Superformula 与 MGM Resorts 的设计团队合作,为所有主要的网络、移动和售票亭体验创建了新的 MGM 设计语言。凭借更小的团队和全新的 Flutter 代码库,他们能够比以前更快地重建应用并发布到两个应用商店,导致 MGM 的预订转化率增加了 9%。

重大變更

與往常一樣,我們儘可能減少每次新發佈 Flutter 時產生的重大變更數量,同時依然需要確保 Flutter 提供直觀、靈活的 API,能夠支援新平台上的新成語。在 去年的使用者調查 中,您告訴我們,您可以容忍經過仔細考慮的重大變更,這些變更會改進框架。因此,我們將繼續逐步改進 API。以下是此版本中的重大變更。

  • #42100 #42100 在使用 pushReplacement(...) 時運行之前路線的輔助動畫
  • #45940 棄用 UpdateLiveRegionEvent
  • #49389 快速捲軸時延遲圖片解碼
  • #49391 文字選取溢出(Android)
  • #49771 #49771 斷言快取提示未針對空繪製器設定
  • #50318 #50318 實時圖片快取
  • #50354 #50354 使用支撐框高度來計算選取矩形,以確保它們保持在可見範圍內
  • #50733 gen_l10n 中生成訊息查找
  • #51435 RouteSettings 中移除 isinitialroute
  • #52781 mouse_tracking.dart 移到 rendering
  • #44930 #44930 Navigator 2.0:重構命令式 API 以繼續在新導航系統中運作

總結

隨著我們的行動支援越來越成熟,同時我們讓 Web 更接近生產品質,Flutter 帶來了解決我們行業數十年來一直在努力解決的一個問題的希望:如何從單個程式碼庫跨多個平台構建出色的應用程式?憑藉 Flutter 提供的所有功能和能力,我們認為我們正在朝着回答這個問題的正確方向前進。您將構建什麼?


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

【文章翻譯】Announcing Dart 2.8

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

宣布 Dart 2.8:更快的套件工具,新的工具確保相依性永不過時

今天,我們宣布推出 Dart SDK 的新版本 2.8。我們持續看到 Dart 社群的驚人成長;現在我們有 數百萬 Flutter 開發人員 使用 Dart 作為他們客戶端優化的語言,在任何平台上構建快速的應用程式。雖然我們仍在努力完成 即將推出的空安全 功能,使 Dart 成為構建快速且穩定的使用者介面的更優化語言,但我們也有一些令人興奮的新功能,專注於讓開發人員在管理相依性時更有效率。

Dart 平台透過 pub 客戶端工具pub.dev 套件儲存庫提供內建的套件管理支援,該儲存庫在過去一年中成長了 200%,現在擁有近 10,000 個套件。作為我們持續改進 Dart 生態系統工作的一部分,Dart 2.8 SDK 為 pub 客戶端工具帶來了兩項改進:pub get 的效能大幅提升,以及一個確保您的套件相依性保持最新的新工具。

Dart 2.8 也在 Dart 語言和函式庫中帶來了一系列小的重大變更。這些變更為我們第一個版本的空安全功能奠定了基礎。

宣布 Dart 2.8:更快的套件工具,新的工具確保相依性永不過時

為空安全鋪路的重大變更

應用程式崩潰的常見原因是嘗試使用恰好為空的變數的程式碼。Tony Hoare 先生於 1965 年在 ALGOL 程式語言中引入了空引用,並在 2009 年的 QCon 演講 中將其稱為他的「十億美元錯誤」。在某些情況下,空值很有用;挑戰在於將這些情況與無用的情況區分開來。在過去的一年中,我們一直忙於在 Dart 中構建對 健全空安全 的支援。這種支援將擴展類型系統以表達始終非空的變數,但此外,類型系統將是完全健全的:Dart 編譯器和執行時將能夠信任這些類型,並且當類型系統保證變數不為空時,能夠產生優化的程式碼。

您可以想像,這是一項龐大的工作,涉及許多活動部件。為了確保我們可以使用清晰的語義構建空安全,我們決定在 Dart 語言和函式庫中進行一些小的重大變更。這些重大變更對 Dart 類型系統的邊緣情況和一些 Dart 核心函式庫進行了細微調整,以確保與可空性相關的優異可用性和效能。我們預計這些重大變更的實際影響(已在 Dart 公告列表上 預先宣布)對於常規應用程式程式碼來說是最小的。如果您在使用 Dart 2.8 時遇到任何問題,我們建議您查看 重大變更 及其描述和緩解步驟。如果這沒有解決問題,請在我們的 問題追蹤器 中提交問題。要預覽空安全體驗,請嘗試 空安全遊樂場

更高品質的套件生態系統

pub 套件管理器和 pub.dev 網站為 Dart 和 Flutter 提供了不斷發展的生態系統。pub.dev 上提供了近 10,000 個套件,我們不斷對正在分享的許多有趣的套件感到驚訝。我們的核心任務是幫助套件作者建立高品質的套件,並幫助應用程式開發人員找到這些套件——例如,我們加入了 改進的 pub.dev 發現驗證的發布者Flutter Favorites

今天,我們宣布從 pub.dev 儲存庫中检索套件的速度大幅提升,並推出一款新工具,幫助您保持所有套件相依性更新。隨著現代應用程式的複雜性,您的應用程式可能依賴於數十個套件。但是,您如何保持所有套件更新到最新版本,以確保您獲得最新的相依性以及所有可用的錯誤修復和效能改進?從 Dart 2.8 開始,您可以使用 pub outdated,這是一個新的工具,用於自動確定哪些相依性需要更新到最新和最好的版本。

Dart 2.8 pub 工具效能改進

我們透過在運行 pub get 時加入對並行擷取套件的支援,以及延遲 pub run 預編譯,優化了 pub 工具的效能。

在對使用 flutter create 建立的新專案運行 flutter pub get 的非正式基準測試中,總執行時間從 Flutter 1.12(使用 Dart 2.7)的大約 6.5 秒減少到 Flutter 1.17(Dart 2.8)的僅 2.5 秒。在像 Flutter gallery 這樣的大型應用程式中,時間從大約 15 秒減少到大約 3 秒!

使用 pub outdated 管理相依性

Dart 程式碼中的相依性會記錄在 pubspec 檔案中。當您透過運行 pub get 命令從 pub.dev 擷取套件時,pub 版本解算器(使用 PubGrub 演算法)會運行一個程序,以確定所有相依性的最新可能版本集,以滿足 pubspec 中列出的約束。請注意,pub 使用單版本方案,其中每個套件都包含在應用程式中單一版本中;這是一項重要的優化,可確保您的應用程式大小盡可能小。

始終使用最新的穩定套件版本是 最佳實務,但這樣做可能很費力。Dart 支援使用 pub upgrade 升級到 語義相容 的最新版本,但您不能在不更新 pubspec 的情況下包含套件的新主要版本。pub outdated 命令透過比較目前使用的版本與 pub.dev 上可用的最新版本,幫助您了解何時有次要版本和主要版本可用。

讓我們考慮一個具體的例子。想像您正在構建一個應用程式,其 pubspec.yaml 包含以下內容:

1
2
3
dependencies:
foo: ^1.3.0
bar: ^2.0.0

您運行 pub get,該工具會建立一個 pubspec.lock 檔案,其中包含以下版本:

1
2
3
4
5
packages:
foo:
version: "1.3.0"
bar:
version: "2.0.1"

幾個月過去了,pub.dev 現在有了 foo (1.3.1) 和 bar (2.1.0 和 3.0.3) 的新版本。您如何發現這些新版本可用?對於次要版本升級(foo 1.4.0 和 bar 2.1.0),您可以運行 pub upgrade,但这不會讓您獲得 bar 3.0.0。要發現新版本,您必須訪問 pub.dev 上的每個套件,並查看其最新版本。或者,您可以使用社群解決方案,例如 Paulina Szklarska 的 版本檢查器 或 Jeroen Meijer 的 pubspec assist

有了 pub outdated,Dart SDK 現在支援版本發現。如果您使用的是支援 Dart 或 Flutter 的 IDE,請在 pubspec.yaml 檔案打開時使用顯示的 Pub outdated 動作。或者,您可以從終端機運行 pub outdated(或 flutter pub outdated):

1
$ pub outdated
1
2
3
4
5
6
7
8
Dependencies            Current  Upgradable  Resolvable  Latest
foo 1.3.0 1.3.1 1.3.1 1.3.1
bar 2.0.1 2.1.0 3.0.3 3.0.3

1 upgradable dependency is locked (in pubspec.lock) to an older version.
To update it, use `pub upgrade`.

1 dependency is constrained to a version that is older than a resolvable version.

此輸出告訴我們,我們可以使用 pub upgrade 自動升級到 foo 1.3.1,這將使我們獲得 foo 的最新可用版本。但它也告訴我們,雖然我們可以自動升級到 bar 2.1.0,但 3.0.3 版本可用。因為升級到 bar 3.0.3 是一個主要版本升級,所以我們需要透過編輯 pubspec.yaml 檔案來選擇加入該升級:

1
2
3
dependencies:
foo: ^1.3.0
bar: ^3.0.3

在我们进行编辑并运行 pub upgrade 之后,pub outdated 現在报告所有相依性都是最新的:

1
$ pub outdated
1
2
Dependencies            Current   Upgradable  Resolvable  Latest
all up-to-date

成功,我們是最新的!因為我們剛剛選擇了新版本,包括一個新的主要版本,我們應該審查這些版本中任何潛在的重大變更,並運行所有測試以確保我們的應用程式仍然按預期工作。

後續步驟

效能改進、重大變更 和新的 pub outdated 命令現已在穩定的 Dart 2.8 SDK 和穩定的 Flutter 1.17 SDK 中提供。我們建議您 立即運行 pub outdated,以查看您的相依性的健康狀況!

如果您發現任何問題,請在 pub 問題追蹤器 中報告,或在 SDK 問題追蹤器 中報告一般問題。我們很樂意聽到您使用 pub outdated 的經驗。在下方留言或向 @dart_lang 發送推文。


宣布 Dart 2.8 最初發佈於 Medium 的 Dart ,人們在那裡透過醒目顯示和回應這個故事來繼續對話。

【文章翻譯】Modern Flutter Plugin Development

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

現代 Flutter Plugin 開發

作者:Amir Hardon, Chris Sells, Collin Jackson, Harry Terkelsen 以及 Matt Carroll

2019 年對於 Flutter Plugin 作者來說是科技進步的一年。我們推出了 Android Plugin API 2.0,它提供了一種更強大、功能更齊全的方式來在您的 Plugin 中實作 Android 支援。我們更新了 pubspec.yaml 格式,使其能夠清楚地指定 Android 和 iOS 支援,以及 Web、macOS、Windows 和 Linux。此外,隨著我們推進 Flutter 以支援多個平台,我們啟用了聯邦化,使具有不同專業知識的多個團隊能夠將其程式碼整合在一起,為使用 Plugin 的 Flutter 開發人員提供無縫的體驗。最後,我們在測試 Plugin 方面取得了長足的進步,還有更多進步即將到來。

Android Plugin API 2.0

在 2019 年 12 月,Flutter 發布了其 Android 嵌入的新版本。這是負責將 Flutter 整合到 Android 應用程式中的 Android 程式碼。它包含像 FlutterActivity、FlutterFragment、FlutterView 和 FlutterEngine 這樣的類別。v2 Android 嵌入包含對標準 Android 生命週期事件的支援,以及將 Flutter 執行與 Android UI 分離,這些功能在 v1 Android 嵌入中缺失。在開發 v2 Android 嵌入的過程中,很明顯現有的 Flutter Plugin API 不足以處理 v2 Android 嵌入的新功能。需要一個新的 Android Plugin API。我們將討論該 API 以及如何使用它。

首先,了解 v2 Android 嵌入中的 FlutterEngine 類別非常重要。FlutterEngine 物件代表一個單一的 Flutter 執行環境。這意味著 FlutterEngine 控制一個 Dart 隔離區(您的 Dart 程式碼,從像 main 這樣的進入點開始)。這也意味著 FlutterEngine 設定了一系列所有 Flutter 應用程式都需要使用的標準平台通道;它包含對平台視圖的支援,它知道如何使用 Flutter UI 繪製紋理,並且它處理執行單個 Flutter/Dart 應用程式的所有其他基本要求。此外,Android 應用程式可能同時包含多個 FlutterEngine。

“將 Plugin 加入”到 Flutter 應用程式中的基本概念是指將該 Plugin 應用到單個 FlutterEngine。例如,如果 Flutter 應用程式需要存取相機,則該功能是透過在特定 FlutterEngine 例項中註冊相機 Plugin 來實現的。此註冊是透過 GeneratedPluginRegistrant 自動完成的,但重要的是要了解每個 FlutterEngine 都維護著自己的 Flutter Plugin 集合。

在舊的 v1 Android 嵌入中,所有 Plugin 都在 Android 應用程式開始時初始化和配置,而且只有一個 Flutter 體驗。在 v2 嵌入中,我們不對 Plugin 何時初始化做任何假設,並且 Plugin 必須在每個 FlutterEngine 中初始化一次。因此,所有適用於 Android 的 Flutter Plugin 現在都必須支援實例化,而不是靜態初始化,並且它們必須支援附加到 FlutterEngine 以及從 FlutterEngine 中分離。以下程式碼範例展示了舊的 v1 Plugin 初始化實作與新的 v2 Plugin 初始化過程之間的差異。

舊的 Plugin 初始化

1
2
3
4
5
6
7
8
class MyOldPlugin {
public static void registerWith(PluginRegistrar registrar) {
// 從 registrar 取得 Plugin 所需的任何參考。
//
// 此 Plugin 現在被視為已“初始化”並“附加”到
// Flutter 體驗。
}
}

新的 Plugin 初始化

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
class MyNewPlugin implements FlutterPlugin {
public MyNewPlugin() {
// 所有 Android Plugin 類別都必須支援無參數
// 建構函式。預設情況下,在沒有宣告的情況下,會提供無參數建構函式,但我們在此為了清晰起見而包含它。
//
// 在這一點上,您的 Plugin 已被實例化,但它
// 尚未附加到任何 Flutter 體驗。您不應嘗試在這裡執行任何與取得
// 資源或操作 Flutter 相關的工作。
}

@override
public void onAttachedToFlutterEngine(FlutterPluginBinding binding) {
// 您的 Plugin 現在已附加到由給定 FlutterEngine
// 表示的 Flutter 體驗。
//
// 您可以透過 binding.getFlutterEngine() 取得關聯的 FlutterEngine
//
// 您可以透過 binding.getBinaryMessenger() 取得 BinaryMessenger
//
// 您可以透過 binding.getApplicationContext() 取得應用程式內容
//
// 您無法在此存取 Activity,因為這個 FlutterEngine 不一定是在
// Activity 中顯示的。有關更多資訊,請參閱 ActivityAware 介面。
}

@override
public void onDetachedFromFlutterEngine(FlutterPluginBinding binding) {
// 您的 Plugin 現在不再附加到 Flutter 體驗。
// 您需要清除在 onAttachedToFlutterEngine() 中建立的任何資源和參考。
}
}

如新的 Plugin API 所示,您的 Plugin 必須等到 onAttachedToFlutterEngine() 完成後才能採取任何有意義的動作,並且它必須透過釋放所有資源來尊重 onDetachedFromFlutterEngine()。您的 Plugin 可能會被附加和分離多次。

此外,您的 Plugin 不應在 onAttachedToFlutterEngine() 中依賴 Activity 參考。您的 Plugin 附加到 Flutter 體驗並不意味著 Flutter 體驗是在 Activity 中顯示的。這是舊的 Plugin API 與新的 Plugin API 之間最重大的差異之一。在舊的 v1 Plugin API 中,Plugin 作者可以依賴於 Activity 立即且永久地可用。這不再正確。

需要存取 Activity 的 Plugin 必須實作第二個名為 ActivityAware 的介面。ActivityAware 介面為您的 Plugin 類別新增回呼,這些回呼告訴您的 Plugin 何時處於 Activity 中、Activity 何時經過配置變更,以及 Plugin 何時不再處於 Activity 中。您的 Plugin 必須尊重這些回呼。以下範例展示了 ActivityAware Plugin 的輪廓:

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
class MyNewPlugin implements FlutterPlugin, ActivityAware {
@override
public void onAttachedToFlutterEngine(FlutterPluginBinding binding) {
// ...
}

@override
public void onDetachedFromFlutterEngine(FlutterPluginBinding binding) {
// ...
}

@override
public void onAttachedToActivity(ActivityPluginBinding binding) {
// 您的 Plugin 現在與一個 Android Activity 關聯。
//
// 如果呼叫此方法,則始終在呼叫
// onAttachedToFlutterEngine() 後呼叫它。
//
// 您可以透過 binding.getActivity() 取得 Activity 參考
//
// 您可以透過 binding.getLifecycle() 監聽生命週期變更
//
// 您可以透過在 binding 上使用適當的方法,監聽 Activity 結果、新的 Intent、使用者
// 離開提示以及狀態儲存回呼。
}

@override
public void onDetachedFromActivityForConfigChanges() {
// 您的 Plugin 與之關聯的 Activity 因配置變更而被銷毀。它會立即回來,
// 但您的 Plugin 必須清除對該
// Activity 和關聯資源的任何參考。
}

@override
public void onReattachedToActivityForConfigChanges(
ActivityPluginBinding binding
) {
// 您的 Plugin 現在已與一個新的 Activity 例項關聯,
// 這是因為配置變更已發生。您現在可以重新建立
// 與 Activity 和關聯資源的參考。
}

@override
public void onDetachedFromActivity() {
// 您的 Plugin 現在不再與 Activity 關聯。
// 您必須清除所有資源和參考。您的
// Plugin 可能會再次與 Activity 關聯,也可能不再關聯。
}
}

新的 Plugin API 明確地認識到 Plugin 可能會或可能不會與 Activity 關聯,並且任何此類 Activity 都可能因配置變更而在任何時候被銷毀和重新建立。這些問題應該對所有 Android 開發人員來說都很熟悉。

為 Flutter 的 v2 Android 嵌入撰寫 Plugin 的關鍵是尊重您的 Plugin 實作的每個 Plugin 生命週期回呼。只要您等到正確的時間建立參考,並在適當的時間釋放那些參考,您的 Plugin 將按預期運作。

一些 Plugin,例如相機 Plugin,只有在 Activity 可用時才有意義。那麼這些 Plugin 要怎麼做呢?如果是只有 UI 的 Plugin,這些 Plugin 可以等到 onAttachedToActivity() 執行後才能執行任何工作。然後,在 onDetachedFromActivity() 中,這些 Plugin 可以清除所有參考,並基本上停用自身。Plugin 不需要在 onAttachedToFlutterEngine() 中做任何特殊事宜。Plugin 只有在附加到 Activity 時才執行工作是可以接受的。

有關如何將您的 Android Plugin 從 v1 API 遷移到 v2 API 的更多詳細資訊,請參閱 flutter.dev 上的 支援新的 Android Plugin API

新的 pubspec 格式

傳統上,Flutter Plugin 是一個單一套件,它讓在 Android 和 iOS 上執行的 Flutter 應用程式能夠存取平台特定的功能;技術上來說,Plugin 由 Dart 程式碼組成,程式碼背後是 Android 特定和 iOS 特定的程式碼。儘管任何 Flutter Plugin 都支援 Android 和 iOS 的假設並不準確(例如,android_intent Plugin 僅支援 Android),但在最初的設計中,Plugin 生態系統是以該假設為基礎。這個假設在很大程度上是正確的,這意味著對於少數幾個錯誤的案例,整體成本很低,而這個簡化的假設則能快速發展並集中焦點。

隨著 Flutter 逐漸支援更多平台,我們決定放棄這個簡化的假設,因為:

  1. 我們預計許多 Plugin 只會支援 Flutter 支援平台的一部分(更何況是下面提到的聯邦化 Plugin)。
  2. 我們想要解鎖需要了解 Plugin 支援平台的工具功能(例如,更智慧的 pub.dev 搜尋和平台感知的工具操作)。

核心缺失的部分是 Plugin 支援哪些平台的清楚指示,因此我們重新設計了 Flutter Plugin 的 pubspec 架構,以圍繞多平台支援。

在之前的 pubspec 架構下,flutter.plugin 鍵包含不同的 Plugin 組態位元,而我們則在 flutter.plugin.platforms 鍵下為每個平台引入了新的鍵,其中包含平台特定的 Plugin 組態。例如,以下展示了支援 Android、iOS、macOS 和 Web 的 Plugin 的 pubspec:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
flutter:
plugin:
platforms:
android:
package: com.example.hello
pluginClass: HelloPlugin
ios:
pluginClass: HelloPlugin
macos:
pluginClass: HelloPlugin
web:
pluginClass: HelloPlugin
fileName: hello_web.dart

environment:
sdk: ">=2.1.0 <3.0.0"
# Flutter 版本在 1.10 之前不支持
# flutter.plugin.platforms 映射。
flutter: ">=1.10.0"

支援這些平台一部分的 Plugin 可以從 platforms 映射中省略平台鍵,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
flutter:
plugin:
platforms:
android:
package: com.example.hello
pluginClass: HelloPlugin
ios:
pluginClass: HelloPlugin

environment:
sdk: ">=2.1.0 <3.0.0"
# Flutter 版本在 1.10 之前不支持
# flutter.plugin.platforms 映射。
flutter: ">=1.10.0"

請注意,使用新的架構時,需要 Flutter SDK 大於 1.10.0,因為這是 Flutter 工具首度支援此架構的版本。

將現有的 Plugin 遷移到新的架構

本節以電池 Plugin 為例,逐步介紹如何將範例 Plugin 從之前的架構遷移到新的架構。

遷移時最重要的是只宣告 Plugin 支援的平台(這在之前是不可能的,這意味著只支援 Android 的 Plugin 必須包含一個無效的 iOS 實作,反之亦然)。

以下是範例 Plugin 的 pubspec.yaml 檔案在遷移之前相關的部分:

1
2
3
4
5
6
7
8
9
10
11
name: sample
version: 0.3.1+5

flutter:
plugin:
androidPackage: io.flutter.plugins.sample
iosPrefix: FLT
pluginClass: SamplePlugin

environment:
flutter: ">=1.6.7 <2.0.0"

假設 Plugin 支援 Android 和 iOS,則升級到新的架構包含以下步驟:

  • 將所需的最小 Flutter 版本提高到 1.10.0(這是首度支援新的架構的版本)。
  • 輕微的版本升級
  • 將 flutter.plugin 中的當前欄位替換為新的 platforms 欄位。
  • 如果之前使用了 iosPrefix 欄位,請重新命名主要的 iOS Plugin 檔案(更多詳細資訊在下面)。

更新後的 Plugin pubspec 如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
name: sample
version: 0.3.2

flutter:
plugin:
platforms:
android:
package: io.flutter.plugins.sample
pluginClass: SamplePlugin
ios:
pluginClass: FLTSamplePlugin

environment:
flutter: ">=1.10.0 <2.0.0"

請注意,由於 Plugin 支援 Android 和 iOS,因此這些是 flutter.plugin.platforms 下唯一的鍵。flutter.plugin.androidPackage 欄位在舊架構中的值成為 flutter.plugin.platforms.android.package 的值。新的架構沒有 iosPrefix 欄位的等效欄位,因為 iOS 的 pluginClass 有專用的鍵,我們可以在 flutter.plugin.platforms.ios.pluginClass 欄位中使用前綴,該欄位設定為 FLTSamplePlugin。

之前使用 iosPrefix 欄位的 Plugin

之前的架構暗示 iOS Plugin 的主介面名稱與其檔案名稱之間存在不一致,例如,對於這個使用之前的架構定義的範例 Plugin,將會有一個 SamplePlugin.h 檔案,該檔案宣告了一個 FLTSamplePlugin 介面。這種不一致不再受支援,這意味著升級到新的架構時,SamplePlugin.h 檔案必須重新命名為 FLTSamplePlugin.h。沒有使用 iosPrefix 鍵的 Plugin 不需要重新命名任何檔案。

有關開發支援任意平台的 Plugin 的更多資訊,請參閱 flutter.dev 上的 開發 Plugin 套件

聯邦化

新的 pubspec 架構不僅允許您精確指定 Plugin 支援的平台,它還讓您能夠將實作分散到多個套件中。過去,Plugin 的 Dart 程式碼、Android Java(或 Kotlin)程式碼以及 iOS Objective-C(或 Swift)程式碼都需要在同一個 Dart 套件中。現在,如果我們想要添加對另一個平台(Web、Mac OS、Windows 等)的支援,它不需要在同一個套件中。分散在多個套件中的 Plugin 被稱為 聯邦化 Plugin

與單一套件 Plugin 相比,聯邦化 Plugin 有幾個優點,包括:

  • Plugin 作者不需要擁有對每個支援的 Flutter 平台(Android、iOS、Web、Mac OS 等)的領域知識。
  • 您可以在不讓原始 Plugin 作者審查和拉取您的程式碼的情況下添加對新平台的支援。
  • 每個套件都可以獨立維護和測試。

那麼,您究竟如何建立 聯邦化 Plugin 呢?讓我們從一些術語開始:

  • 應用程式面向套件: 這是您在應用程式中導入以使用 Plugin 的套件。例如,package:url_launcher 就是一個應用程式面向套件。應用程式面向套件宣告應用程式面向 API,並與各種 平台套件 合作執行平台特定的功能。
  • 平台套件: 這是實作 應用程式面向套件 所需的平台特定功能的套件。例如 package:url_launcher_web:這個套件由 package:url_launcher 使用,在 Web 平台上運行 Flutter 應用程式時啟動 URL。平台套件不應在應用程式中導入,它們僅供 應用程式面向套件 用於呼叫所需的平台特定程式碼。
  • 平台介面套件: 這是將 應用程式面向套件平台套件 整合在一起的黏合劑。應用程式面向套件 宣告可以在 Flutter 應用程式中呼叫的 API,而 平台介面套件 則宣告每個 平台套件 必須實作的介面,以便支援 應用程式面向套件。讓單一套件定義此介面可以確保所有 平台套件 都以統一的方式實作相同的功能。

上圖顯示了應用程式、應用程式面向套件、平台套件和平台介面套件之間的相依性圖。應用程式始終只導入應用程式面向套件(在本例中為 package:url_launcher)。

平台介面 如何將應用程式面向套件與正確的平台套件整合在一起?過去,沒有“平台套件”,只有 Android 程式碼的子資料夾,以及 iOS 程式碼的另一個子資料夾。應用程式面向套件透過 MethodChannel 與平台程式碼通訊。您可以將 MethodChannel 視為事實上的“平台介面”,因為應用程式面向套件呼叫 MethodChannel,而對應的平台程式碼必須在 MethodChannel 上監聽具有正確參數的正確方法。沒有辦法靜態地確認 Android 程式碼或 iOS 程式碼是否正在監聽正確的 MethodChannel 呼叫。

啟動 URL 的舊方法

1
2
3
4
5
Future<void> launch(String url) {
channel.invokeMethod('launch', {
'url': url,
});
}

在聯邦化 Plugin 架構中,平台介面套件 替換了 MethodChannel。應用程式面向套件從平台套件需要的平台特定功能被封裝在平台介面中。在我們的範例中,應用程式面向套件是 package:url_launcher,它唯一需要的平台特定功能是能夠在給定平台上啟動 URL。一個(非常)簡單的平台介面將看起來像這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
abstract class UrlLauncherPlatform {
/// 啟動給定的 [url]。
Future<void> launch(String url);

/// 此平台介面的實時“例項”。
///
/// 這是在註冊平台套件時設定的,
/// 通常是在平台初始化時。
///
/// 例如,Web 平台套件
/// (package:url_launcher_web) 將使用一個
/// 在新標籤頁中啟動 URL 的實作擴展此類別,
/// 並在初始化時將自己設定為實時
/// 例項,方法是:
///
/// UrlLauncherPlatform.instance = WebUrlLauncher();
static UrlLauncherPlatform instance;
}

現在,應用程式面向套件不再呼叫 MethodChannel,而是呼叫平台介面。

啟動 URL 的新方法

1
2
3
Future<void> launch(String url) {
return UrlLauncherPlatform.instance.launch(url);
}

因此,應用程式面向套件 呼叫 平台介面。平台介面如何與 平台套件 整合在一起?平台套件實作平台介面,並在平台初始化時將自己註冊為平台介面的預設例項。

例如,如果我們想要撰寫 package:url_launcher_web,我們只需要撰寫一個擴展 UrlLauncherPlatform 的類別,並為 Web 平台啟動 URL。程式碼將看起來像這樣:

1
2
3
4
5
6
7
8
9
10
class UrlLauncherWeb extends UrlLauncherPlatform {
/// Web 平台在應用程式初始化時會自動呼叫此方法。
static void registerWith(Registrar registrar) {
var webLauncher = UrlLauncherWeb();
UrlLauncherPlatform.instance = webLauncher;
}

@override
Future<void> launch(String url) => window.open(url, '');
}

遷移到聯邦化 Plugin 架構的好處是,一旦您設定了應用程式面向套件和平台介面套件,添加對新平台的支援就變得非常簡單(而且您甚至不需要親自做!)。所有需要做的就是建立一個新的平台套件,該套件擴展平台介面套件中宣告的平台介面。

有關聯邦化 Plugin 的更多詳細資訊,請參閱 flutter.dev 上的 聯邦化 Plugin

測試 Plugin

在撰寫新的跨平台 Plugin 或為現有的 Plugin 添加平台時,您可以透過撰寫測試來節省時間和避免未來的麻煩。自動化測試可以保護您的 Plugin 免受功能性回歸,讓您能夠快速開發新功能並合併貢獻。

一個經過充分測試的 Plugin 通常包含分散在多個套件中的幾種測試風格。您可能會因為撰寫不穩定或不太可能失敗的測試而降低效率,因此請專注於讓您相信關鍵用例仍然具有功能的測試撰寫。

AutomatedWidgetsFlutterBinding 測試

使用 AutomatedWidgetsFlutterBinding 運行的測試在開發機器上運行,而不是在設備或瀏覽器上運行。因此,它們運行速度更快,並且某些功能需要由模擬提供。

在應用程式面向套件中(例如 myplugin),套件的單元測試確保對應用程式面向 API 的呼叫會導致與平台介面套件的預期交互。這些測試通常導入 package:mockito 來提供一個虛假的平台介面,並驗證它是否接收到正確的呼叫。以下是一個來自 package:url_launcher 的 範例測試

1
2
3
4
5
6
test('returns true', () async {
when(mock.canLaunch('foo')).thenAnswer((_) =>
Future<bool>.value(true));
final bool result = await canLaunch('foo');
expect(result, isTrue);
});

在平台介面套件中(例如 myplugin_platform_interface),平台介面是一個抽象類別,無法直接實例化。但是,平台介面套件通常也包含平台介面的方法通道實作,因此這就是您應該測試的內容。此套件的測試應專注於從對平台介面的呼叫產生的方法通道呼叫以及方法通道。這些測試通常使用 setMockMethodCallHandlerisMethodCall 匹配器來驗證行為。

1
2
3
4
5
6
7
8
9
10
11
test('canLaunch', () async {
await launcher.canLaunch('http://example.com/');
expect(
log,
<Matcher>[
isMethodCall('canLaunch', arguments: <String, Object>{
'url': 'http://example.com/',
})
],
);
});

在平台測試中(例如 myplugin_web),您可以利用平台特定的功能。在當前的 Flutter SDK 中,flutter test 提供一個實驗性的 –platform 標誌,讓您能夠選擇在一個類似的 Chrome 環境中運行測試,該環境可以使用 dart:html。

這個測試模式對於在平台實作套件(例如,myplugin_web)中撰寫測試很有用。

1
2
3
test('cannot launch "tel" URLs', () {
expect(canLaunch('tel:5551234567'), completion(isFalse));
});

此外,您可以使用 針對 Web 的實驗性 ‘flutter drive’ 測試支援 在 Chrome 中運行您的 GUI 測試。

有關 Plugin 測試的更多資訊,請參閱 flutter.dev 上的 測試您的 Plugin

結論

如您所見,Flutter Plugin 開發人員有很多新功能,讓您能夠在更多平台上建立功能更齊全、更強大的 Plugin。如果您有興趣了解一些 Web 特定的細節,我推薦 Harry Terkelsen 的兩部分系列文章,如何撰寫 Flutter Web Plugin第 1 部分第 2 部分)。有關一般 Plugin 撰寫的更多資訊,flutter.dev 上的 開發 Plugin 套件 文件也是一個很好的資源。


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

【文章翻譯】Flutter web support updates

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

Flutter Web 支援更新:PWA、改進的除錯和基於 CanvasKit 的渲染

快速說明:我們原本計畫今天發佈 Flutter 的新穩定版本,但我們在最終測試中發現了一個 崩潰錯誤,我們希望在發佈之前解決這個錯誤。我們已將修復程式碼提交到程式碼庫中,但我們希望讓它多燒一點時間,以確保我們沒有引入新的迴歸。我們現在計畫在下週發佈穩定版本。感謝許多報告問題並與我們合作重現問題的人。

大約一年前,我們分享了 Flutter 支援目標網頁的第一個技術預覽。我們對 Flutter 的願景是提供一個可移植的工具包,用於在任何您想要在螢幕上繪製像素的地方建立美麗的體驗。鑑於網頁的普及程度,它是嘗試使用 Flutter 的自然場所,但它的特性與 Android 和 iOS 等行動平台不同。

在 Web 瀏覽器中運行 Flutter 程式碼開闢了一系列引人入勝的可能性,包括:

  • 為開發人員提供一條便捷的途徑,將他們現有的行動應用程式移植到網頁上,無論是作為完整的應用程式、PWA,還是作為已安裝體驗的低摩擦預覽。
  • 使桌面應用程式開發人員能夠一次構建,然後在最後決定是作為高性能已安裝桌面應用程式和/或無摩擦的瞬時 Web 應用程式發佈。
  • DartPadCodePen 這樣的工具,讓您能夠快速嘗試 Dart 程式碼,並實時查看結果,而無需在您的機器上本地安装任何東西。
  • 在行動和 Web 應用程式之間共享程式碼,而几乎没有妥協。

Flutter 旨在在其框架和 API 選擇方面堅決以應用程式為中心。我們相信它是一個獨特的產品,有助於豐富的 Web 開發。如果我們成功地達成了我們的目標,Flutter 的 Web 支援將提供一個能夠充分利用 Web 平台提供的所有功能的框架,讓開發人員可以構建美觀的豐富互動式應用程式,這些應用程式在瀏覽器中完全如同家一般。

Ampstor:一個使用 Flutter 的 Web 支援構建的無程式碼工具,用於構建更好的講故事的 Web 頁面。

Ampstor 是一個強大的範例。該團隊著手構建一個用於構建 AMP 故事 的編輯工具,AMP 故事是一種新格式,可以幫助內容提供者為行動設備創建視覺敘事。以下是他們關於在 Web 上使用 Flutter 的觀點:

「作為一家幫助客戶構建高度品牌化體驗的初創公司,我們必須能夠快速進入市場並根據客戶回饋進行迭代。我們選擇 Flutter 是因為它讓我們能夠專注於產品,而不必專注於平台本身。到目前為止,我們對自己的體驗非常滿意,我們的客戶也喜歡我們構建的應用程式。」

我們在 Web 支援方面的早期目標是證明 Flutter 架構可以移植到 Web 上的基本假設。去年夏天,我們將 Web 程式碼重新併入主框架,從而產生了一個支援行動和 Web(以及桌面!)的單個程式碼庫。從那以後,我們的重點轉移到使用最新的 Web 平台進展來提供快速、無卡頓的性能,改進跨 Flutter 目標平台的行为一致性,以及根除并解决在 Web 上還不自然的場景。

考慮到這一點,以下是團隊自去年 12 月在 beta channel 中推出 Web 支援以來一直在努力的事項。

有什麼新功能?

PWA 支援

漸進式 Web 應用程式 (PWA) 的粉絲會很高興地知道,現在 Web 應用程式的預設 Flutter 範本包括支援 PWA 應用程式所需的安裝、離線功能等核心功能:

The Flutter “counter” template app, running as a PWA on macOS.
The Flutter “counter” 範本應用程式,在 macOS 上作為 PWA 運行。

請注意,這看起來像是一個普通的桌面應用程式,但它實際上是一個已安裝到瀏覽器中作為 PWA 的 Flutter Web 應用程式。您可以看到標題列中新增的圖示按鈕的區別,這些按鈕公開了 Chrome 功能。基於 Flutter 的 PWA 可以 與其他基於 Web 的 PWA 以相同方式安裝;表明您的 Flutter 應用程式是 PWA 的設定由 manifest.json 提供,manifest.jsonflutter createweb/ 子資料夾中產生。

PWA 在主要的行動和桌面瀏覽器上受支援,僅僅是我們正在投資 縮短原生平台和 Web 之間差距 的方式之一。

這仍然是一個正在進行的工作,因此如果您看到任何看起來不正確的地方,請給我們回饋。

Plugin

當我們推出 beta 版本時,只有少數 Plugin 支援 Web。從那以後,我們看到 更多 Plugin 在 Web 平台上加入了支援,這要歸功於社群的一些重大貢獻,例如:

  • audioplayers:同時播放多個音訊檔案。
  • connectivity:允許應用程式發現網路連線情況並相應地配置。
  • cloud_firestore:無 SQL 雲資料庫,透過 Cloud Firestore API 存取。
  • cloud_functions:存取無伺服器雲功能,由 Firebase 托管。

儘管我們計畫為 Web 平台擴展更多由 Google 編寫的熱門 Plugin,但我們在很大程度上依賴社群幫助為現有的套件提供 Web 支援。為了幫助您入門,我們發佈了幾篇文章,說明 如何撰寫 Web Plugin

使用運算式評估進行 Web 除錯

除錯是開發過程的一個關鍵步驟,也是為建立更豐富的 Flutter Web 體驗而積極開發的領域之一。如果您使用 VS Code 開發 Flutter 應用程式,您可能期望所有除錯功能都可用,包括在除錯視窗中評估運算式,使用監視功能,或將滑鼠懸停在變數上以查看其值。

到最近,這僅適用於除錯行動 Flutter 應用程式。在過去的幾個月裡,我們一直在努力將運算式評估連接到 Web 上的 Flutter,現在已準備好嘗試在 VSCode 中使用實驗性支援。對其他 IDE(如 IntelliJ)的支援仍在進行中。

此功能可在 Flutter dev 或 master channel 上使用。請使用以下說明在 VS Code 中設定它:

將您的專案配置為 在 Web 上運行。在 VS Code 中,在 launch.json 中為 Web 啟用運算式評估:

launch.json file

設定斷點並開始除錯。嘗試使用以下所示的運算式評估框在除錯主控台中使用運算式評估。此外,您可以使用監視視窗或將滑鼠懸停在變數上以使用運算式評估程式碼。

debug console

您也可以嘗試使用運算式設定條件斷點:

breakpoint evaluation

測試

Flutter 的最新版本現在支援以與行動應用程式相同的方式,對您的 Flutter Web 應用程式運行自動化的 Flutter Driver UI 測試。若要啟用此實驗性功能的支援,您可以 閱讀 Flutter wiki 上的說明

目前的優先事項:測試、品質和效能

在過去的幾個月裡,我們的重點一直放在改進 Flutter Web 支援的基础结构、品質和渲染。雖然我們還有很多工作要做,但我們在所有三個領域都取得了重大進展。

測試基础结构和基準測試

構建測試基础结构是那些對於使用 Flutter 開發的人來說並不總是顯而易見但對於在 Web 上構建穩定版本的 Flutter 非常重要的改進之一。

在過去的季度中,我們添加了自動測試以確保 Web 引擎和框架的準確性。我們在 Chrome 上添加了螢幕截圖測試,以確保在我們對程式碼進行更改時,我們的渲染一致且正確。我們現在在 Chrome、Firefox 和 Safari 上運行單元測試和整合測試。在行動瀏覽器和 Internet Explorer/Edge 上運行這些測試將很快上線。此外,我們添加了性能基準測試,使我們能夠快速迭代而不會引入迴歸。

品質

說到問題,我們一直在努力解決問題,透過優化和準確性修復逐步提高我們的品質。

最近,我們在 Flutter for Web 中進行了優化,改進了靜態內容的捲軸速度,這意味著不是懶載入的內容,而是在同一畫面中全部渲染的內容。這應該會使捲軸性能更符合傳統的 Web 體驗。

我們還修復了幾個文字渲染問題,例如文字消失在畫布後面或多行文字的游標問題。這些文字準確性修復目前在 FLUTTER_WEB_USE_EXPERIMENTAL_CANVAS_TEXT 旗標後面啟用;我們的計畫是很快將其設為平台預設值。

在去年的 Flutter Interact 中,我們宣布我們新的 Gallery 範例應用程式使用 Flutter 在 Web 上運行。從那以後,我們對 Web 支援和應用程式都進行了重大的性能優化,使其運行得更快更順暢。由於這些變化,初始載入時間現在快了三倍,程式碼大小減少了 2.7 倍。

Flutter Gallery app
Flutter Gallery 應用程式,在 Windows 上作為 PWA 運行。

請繼續關注即將推出的部落格系列,該系列將分享更多我們學到的知識,以及優化 Flutter 應用程式性能的最佳實務。在此之前,請親自查看更新的 Flutter Gallery 應用程式

使用 CanvasKit 改進渲染

當我們開始探索 Web 時,我們考慮了各種 UI 渲染方法,知道我們的選擇將決定我們在 Web 上構建、佈局和繪製 Flutter Widget 的程度。

我們最初選擇了基於 HTML DOM 的模型,該模型結合使用 HTML、CSS 和 Canvas API 來表達 Flutter 架構產生的輸出。我們將此實作稱為 DomCanvas 渲染系統。

在過去的一年中,我們對 Web 上可用的各種渲染技術的性能和準確性特性有了更深入的了解,並且一直在嘗試使用 CanvasKit 的第二種方法。CanvasKit 使用 WebAssemblyWebGLSkia 帶到 Web 上,啟用一個硬體加速的繪圖表面,它可以提高我們有效渲染複雜和密集圖形的性能。

我們認為 DomCanvas 和 CanvasKit 在不同的場景下都具有優勢,因此我們支援這兩種渲染模型。

  • DomCanvas 提供最大的瀏覽器兼容性,程式碼大小緊湊。但是,重新繪製性能較差,這使得它不太適合更注重圖形的應用程式。
  • CanvasKit 後端提供卓越的性能、保真度和準確性,但由於程式碼大小較大,其初始啟動時間較差。

預設情況下,Flutter 的 Web 支援使用 DomCanvas,但您可以使用以下命令啟用 CanvasKit 渲染引擎:

1
2
flutter run -d chrome --release
--dart-define=FLUTTER_WEB_USE_SKIA=true

請注意:CanvasKit 引擎仍然存在一些粗糙的地方;對於將 Flutter 部署到生產環境的早期採用者來說,DomCanvas 引擎提供了最大的穩定性。

我們雷達上的已知問題

雖然我們相信我們在構建堅實基础方面取得了良好的進展,但我們計劃在以下幾個領域继续努力,以完善我们的 Web 支援。

  • 桌面級 UX。在桌面 Web 瀏覽器上支援 Flutter 需要的不僅僅是 Web 支援:行動和桌面體驗之間通常存在著重大的 UI 差異。因此,我們添加了支援桌面級體驗的功能,例如 響應式 Widget 和捲軸物理,這些功能仍在實作中。
  • 自動填寫 是大多數平台支援的功能,就 Web 而言,它意味著啟用瀏覽器儲存資料并在適當的地方填寫資料。我們 最近為核心框架添加了初始的自動填寫支援,現在我們正在努力 將此功能添加到 Web 上的 Flutter。此工作包括解決瀏覽器差異,為未來的自動填寫儲存填寫資訊 以及將可自動填寫的元素作為一組傳遞給引擎。
  • URL 路由。這可能是一個小問題,但今天 Flutter Web 應用程式即使在首頁上也包含了一個 用於路由的雜湊標記。我們正在努力消除這個問題,并提供對 URL 路由的更多控制,目前正在審查 社群的貢獻
  • 程式碼大小 仍然是我們積極調查的領域。今天,每個 Flutter Web 應用程式都下載了它需要的引擎程式碼;我們正在研究將一些邏輯快取起來以減少啟動時間和下載大小的可能性。

行動呼籲

我們希望這篇文章中有一些有趣的新發現:自 12 月更新以來,我們取得了很大進展。但是,您应该注意,Flutter 的 Web 支援仍然处于 beta 阶段,您應該謹慎地將其部署到生產環境中,因為性能和瀏覽器兼容性工作還在進行中。

如果您是使用 Flutter 在 Web 上開發的新手,請查看 flutter.dev/web 了解詳情,并在 dartpad.devcodepen.io/flutter 上嘗試您的程式碼。

對於那些目前正在開發 Flutter 網頁應用程式的人,感謝您的支持和貢獻,我们希望您繼續嘗試、提交錯誤更新 Plugin 以支援 Web!


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

【文章翻譯】Introducing Google Fonts for Flutter v 1.0.0!

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

介紹 Flutter 的 Google Fonts v 1.0.0!

Anthony RobledoMH Johnson 撰寫

Flutter 的 Google Fonts 最初於 2019 年 12 月的 Flutter Interact 上宣布。在社群的幫助下(感謝大家!),我們添加了許多新功能並提高了穩定性。在 Beta 測試取得成功後,它現在已準備好作為 Google Fonts 1.0.0 在 Android、iOS、Web 和 MacOS 上投入使用。

自 Beta 測試以來添加的功能:

  • 使用 GoogleFonts.asMap() 獲取所有支援字體的清單
  • 使用動態名稱的字體:GoogleFonts.getFont('Lato')
  • 使用校驗和驗證確保安全下載
  • 體驗效能提升,特別是在 Web 上
  • 使用 pubspec 資產預捆綁字體
  • 選項性地停用在執行時提取字體(在偵錯或預捆綁時有用)

Google Fonts 允許開發人員輕鬆地在他們的應用程式中嘗試和使用 fonts.google.com 上的任何字體。當應用程式準備發佈時,開發人員可以決定使用者是透過從 API 下載字體來接收字體,還是將字體預捆綁到應用程式套件中。

我們已將預捆綁字體所需的工作減至最少,並且使其與動態字體載入相容,因此如果您決定預捆綁,您無需更改程式碼。

請查看 入門指南,以了解如何透過 TextStyles 或 TextThemes 將字體包含到您的應用程式中。當您準備發佈時,請了解如何選項性地預捆綁字體並將字體許可添加到應用程式的 LicenseRegistry 中。

我們希望您發現此套件對您所有 Google Fonts 的需求都很有用。與往常一樣,請隨時留下回饋、提交議題 或開啟提議請求!


介紹 Flutter 的 Google Fonts v 1.0.0! 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

【文章翻譯】Flutter Spring 2020 Update

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

Flutter 2020 年春季更新

作者:Tim Sneath 和 Patrick Sosinski

對於我們 Google 的工程師、產品經理、使用者體驗研究員、技術作家和開發者關係工程師來說,過去幾個月充滿了各種挑戰;就像你們中的大多數人一樣,我們都在努力應對與平常非常不同的日常工作。儘管如此,作為一個開源專案,我們能夠繼續使用公開可用的工具進行開發。在有時具有挑戰性,當然也是新穎的工作環境的限制下,以及需要照顧家人的需求,我們正在繼續根據我們的春季積壓工作取得進展,並且也從「外部」學習了許多關於貢獻的知識!我們希望您也能取得進展,並保持健康和安全。

Flutter 的發展勢頭

我們繼續看到 Flutter 使用率的快速增長,自我們發佈以來,在過去的十六個月中,已有超過 兩百萬開發人員使用 Flutter。儘管遇到了這些前所未有的情況,但在 3 月份,我們看到 Flutter 的月度增長率為 10%,目前每月有近 50 萬開發人員使用 Flutter。

以下是一些其他有趣的統計數據:

  • 您中有 60% 使用 Windows 開發,27% 使用 macOS,13% 使用 Linux。
  • 您中有 35% 為新創公司工作,26% 是企業開發人員,19% 是自雇人士,7% 為設計公司工作。
  • 78% 的 Flutter 開發人員使用 stable channel,11% 使用 beta,11% 使用 dev 或 master。
  • Flutter 的前五大地區是印度、中國、美國、歐盟和巴西。
  • Play 商店中大約有 50,000 個 Flutter 應用程式,僅在過去一個月就上傳了近 10,000 個。
  • Flutter 應用程式中使用最多的框架套件是 httpshared_preferencesintlmetapath_providerpedantic
  • Flutter 應用程式中使用最多的第三方套件是 providerrxdartcached_network_imagesqflitefont_awesome_flutterflutter_launcher_icons

Flutter 在企業中的應用

Flutter 的使用率在企業客戶中特別快速增長,我們的研究繼續表明,能夠構建高度品牌化的體驗並支援多個平台是大型公司選擇 Flutter 的主要原因。一個最近的例子是 Nubank,它是亞洲以外最大的數字銀行,擁有超過 2000 萬客戶。在對應用程式開發的選擇進行了 詳細的調查和分析 後,Nubank 選擇了 Flutter,並且自那以後,他們能夠將其前端開發團隊統一到單一框架上,使他們能夠在 iOS 和 Android 上同時發佈新功能。

請查看下面的開發者故事,其中解釋了他們使用 Flutter 所看到的一些好處:

企業的一個常見需求是專業組件。我們正在與 SyncFusion 合作,他們的 Essential Studio 產品現在包含一系列 高品質的 Flutter 組件,包括圖表、PDF 操作和條碼生成。憑藉他們的 2020.1 版本,所有組件都開箱即用地支援 Android、iOS Web,並且他們現在有了一個 基於 Web 的控制項預覽

更新我們的發佈流程

最後,當我們展望我們的下一個穩定版本時,我們想分享一些我們正在對我們的發佈模型所做的更改,我們認為這些更改將進一步提高發佈版本的穩定性和可預測性。

今天的發佈流程旨在簡單且低維護。當我們還是一個規模較小的團隊和一個比較新的框架時,這個流程讓 Flutter 得益匪淺,但是隨著我們目前的規模,我們遇到了一些影響 Flutter 貢獻者和開發者的問題,包括:

  • 對於何時構建版本,以及版本中包含的程式碼,缺乏明確性
  • 缺乏分支測試導致熱修復版本出現回歸

從 4 月份的 Flutter 版本開始,我們將轉移到一個分支模型,其中包含一個用於 beta 和穩定版本發佈的穩定階段。我們現在將在每個月初為 beta 版本創建分支,並透過 cherry-picking 任何關鍵修復來穩定此版本。大約每季度一次,當前的 beta 分支將被提升到我們的穩定版本。如果有必要,我們將繼續在此版本上進行熱修復。我們的基礎架構現在支援針對分支進行測試,這意味著我們可以驗證 cherry-pick,並且會根據嚴重程度接受一些請求。

我們還藉此機會調整了 Flutter 和 Dart 發佈流程和通道。因此,Dart 添加了 beta 通道,並且未來發佈將同步(例如,Flutter beta 版本將包含一個 Dart beta 版本)。

如果您已經在發佈基於穩定通道的 Flutter 應用程式,我們鼓勵您在 beta 候選版本上測試您的應用程式,並透過報告任何問題來影響穩定版本發佈的品質。您也可以按照 Flutter wiki 上新的 Flutter cherry-pick 流程 在穩定通道上升級回歸或阻止錯誤。

我們認為,這個新流程既能提高我們對版本品質和可預測性的信心,也能提供一種更簡單的方法來將熱修復傳遞到穩定通道。

版本控制變更

作為此分支模型的一部分,我們對版本的版本控制方式進行了一些細微調整。

完整的技術細節可以在 Flutter 構建釋出通道 wiki 頁面上找到;以下是一個簡短的摘要:

  • 非穩定發佈版本 將在版本字串中使用 .pre 來註解,以表示其預發佈狀態。給定一個版本字串 x.y.z-n.m.pre,每次從 master 構建一個新的 dev 通道構建時,n 都會遞增。

    • 1.18.0–1.0.pre:master 移動到 1.18 後的第一個 dev 版本
    • 1.18.0–2.0.pre:從 master 上更近的點開始的下一個 dev 版本
  • beta 版本 將從 dev 版本點構建,如上所述。當我們對這些版本中的某個版本進行 cherry-pick 時,m 版本將遞增。例如,如果我們從 master 中選取第 15 個 dev 版本作為我們的 1.18 beta 版本,版本控制將如下所示:

    • 1.18.0–15.0.pre:初始的 beta 候選版本(與發佈到 dev 的版本相同)
    • 1.18.0–15.1.pre:在(現在的)beta 分支上進行一些 cherry-pick 的後續構建
    • 1.18.0–15.2.pre:第二次後續構建
  • 穩定版本 將被版本控制為 x.y.0。如果有必要,後續的熱修復版本將遞增修補程式號碼。x.y.1、x.y.2 等)

    • 1.18.0–15.4.pre:分支上的最後一個 beta 版本
    • 1.18.0:穩定版本,與 1.18.0–15.4-pre 相同的位元
    • 1.18.1:1.18.0 的潛在熱修復

接下來是什麼?

我們使用這個新版本控制模型的第一個版本將是 我們的下一個穩定版本,我們計劃在下週發佈。到時再來查看所有新功能的完整概述。

在此期間,請查看我們在過去幾週中發佈的一些其他公告。上週,我們宣布了 Flutter 的 CodePen 支援。我們很享受查看不同創建者在過去幾天中構建的筆刷。以下是一些我們最喜歡的:

如果您正在尋找 Flutter 學習資源,我們現在提供了一個 免費的線上 Flutter 入門培訓課程。這個由 Angela Yu 講授的十小時課程提供了教學和實驗室,幫助您開始您的 Flutter 旅程。

下週見。在此期間,請待在家中,保持健康!


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

【文章翻譯】Animation deep dive

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

動畫深探

去年我錄製了 Flutter 動畫系列中的其中一集,我想我會將相同的內容發佈給那些喜歡文字勝於視頻的人。

在這個系列的其他集中,我的同事們討論了在 Flutter 中建立動畫的所有實際方法。在我的那一集裡不是這樣。在這裡,您將學習如何以最不務實的方式實現動畫。(但同時您也會學到一些東西。)

讓我們從簡單輕鬆的東西開始:

什麼是運動?

你看,運動是一種錯覺。看看這個:

Filip 揮手的视频。

這是一個謊言。您實際上看到的是許多靜止的圖片以快速連續的方式顯示。這就是電影運作的方式。在電影中,這些單獨的圖片稱為幀,因為數位螢幕的運作方式類似,所以這裡也稱為幀。電影通常每秒顯示 24 幀。現代數位設備每秒顯示 60 到 120 幀。

因此,如果運動是謊言,那麼所有這些 AnimationFoo 和 FooTransition Widgets 到底在做什麼?當然,因為幀需要每秒構建高達 120 次,所以 UI 不能每次都重新構建。

還是可以?

事實上,Flutter 中的動畫只不過是在每一幀上重新構建 Widget 樹的一部分的方法。沒有特殊情況。Flutter 足夠快可以做到。

讓我們看看 Flutter 動畫的基礎構建塊之一:AnimatedBuilder。此 Widget 是一個 AnimatedWidget,它由 _AnimatedState 支援。在 State 的 initState() 方法中,我們正在監聽動畫(或者,如這裡所稱的 Listenable),當它更改其值時,我們會… 呼叫 setState()。

這段令人困惑的螢幕錄影只是顯示,我在前一段中所說的是真的。Animated Builder 確實會在每一幀上呼叫 setState()。

這就是了。Flutter 中的動畫只不過是快速連續地更改某個 Widget 的狀態,每秒 60 到 120 次。

我可以證明。這是一個從零到光速「動畫」的動畫。雖然它在每一幀上都更改文字,但從 Flutter 的角度來看,它只不過是另一個動畫。

讓我們使用 Flutter 的動畫框架從頭開始建立這個動畫。

通常,我們會使用 TweenAnimationBuilder Widget 或類似的東西,但在本文中,我們將忽略所有這些,而使用計時器、控制器和 setState。

計時器

讓我們先談談計時器。99% 的時間,您不會直接使用計時器。但是,我認為談談它仍然很有幫助,即使只是為了揭開它神秘的面紗。

計時器是一個針對每一幀呼叫函數的物件。

var ticker = Ticker((elapsed) => print('hello'));
ticker.start();

在本例中,我們會在每一幀中列印「hello」。誠然,這並不十分有用。

此外,我們忘記呼叫 ticker.dispose(),所以現在我們的計時器將永遠持續下去,直到我們關閉應用程式。

這就是為什麼 Flutter 會提供 SingleTickerProviderStateMixin,這個恰如其分的 Mixin 您在之前的幾個視頻中都有看到。

這個 Mixin 負責管理計時器的麻煩。只需將它添加到您的 Widget 的狀態中,現在您的狀態就默默地成為了 TickerProvider。

class _MyWidgetState extends State<MyWidget> 
with SingleTickerProviderStateMixin<MyWidget> {
  @override
Widget build(BuildContext context) {
return Container();
}
}

這意味著 Flutter 框架可以向您的狀態請求計時器。最重要的是,AnimationController 可以向狀態請求計時器。

class _MyWidgetState extends State<MyWidget>
with SingleTickerProviderStateMixin<MyWidget> {
AnimationController _controller;
  @override
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
}
  @override
Widget build(BuildContext context) {
return Container();
}
}

AnimationController 需要計時器才能運作。如果您使用 SingleTickerProviderStateMixin 或其近親 TickerProviderStateMixin,您可以直接將其提供給 AnimationController,這樣就完成了。

AnimationController

AnimationController 是您通常用於播放、暫停、反轉和停止動畫的工具。與純粹的「滴答」事件不同的是,AnimationController 在任何時候都會告訴我們動畫的 進展 到達了哪一點。例如,我們是否已經走了一半?我們是否已經走到了 99%?我們是否已經完成動畫?

通常,您會使用 AnimationController,然後使用 Curve 進行轉換,再透過 Tween 處理,最後在其中一個方便的 Widget(如 FadeTransition 或 TweenAnimationBuilder)中使用它。但是,出於教學目的,我們不會這樣做。相反,我們將直接呼叫 setState。

setState

在我們初始化 AnimationController 之後,我們可以為它添加一個監聽器。在這個監聽器中,我們會呼叫 setState。

class _MyWidgetState extends State<MyWidget>
with SingleTickerProviderStateMixin<MyWidget> {
AnimationController _controller;
  @override
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
_controller.addListener(_update);
}
  void _update() {
setState(() {
// TODO
});
}
  @override
Widget build(BuildContext context) {
return Container();
}
}

現在,我們可能應該有一個要設定的狀態。讓我們使用一個整數來保持簡單。而且不要忘記在我們的 build 方法中實際使用狀態,並根據控制器的當前值在我們的監聽器中更改狀態。

class _MyWidgetState extends State<MyWidget>
with SingleTickerProviderStateMixin<MyWidget> {
AnimationController _controller;
  int i = 0;
  @override
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
_controller.addListener(_update);
}
  void _update() {
setState(() {
i = (_controller.value * 299792458).round();
});
}
  @override
Widget build(BuildContext context) {
return Text('$i m/s');
}
}

此程式碼根據動畫的進度將值從零分配到光速。

執行動畫

現在,我們只需要告訴動畫它應該花多長時間完成,並啟動動畫。

class _MyWidgetState extends State<MyWidget>
with SingleTickerProviderStateMixin<MyWidget> {
AnimationController _controller;
  int i = 0;
  @override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
_controller.addListener(_update);
_controller.forward();
}
  void _update() {
setState(() {
i = (_controller.value * 299792458).round();
});
}
  @override
Widget build(BuildContext context) {
return Text('$i m/s');
}
}

Widget 在被添加到螢幕上的那一刻就會開始動畫。它會在 1 秒內從零到光速「動畫」。

釋放控制器

哦,別忘了釋放 AnimationController。否則您的應用程式中就會有記憶體洩漏。

class _MyWidgetState extends State<MyWidget>
with SingleTickerProviderStateMixin<MyWidget> {
AnimationController _controller;
  int i = 0;
  @override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
_controller.addListener(_update);
_controller.forward();
}
  @override
void dispose() {
_controller.dispose();
super.dispose();
}
  void _update() {
setState(() {
i = (_controller.value * 299792458).round();
});
}
  @override
Widget build(BuildContext context) {
return Text('$i m/s');
}
}

也許直接使用內建的 Widget?

正如您所見,自己做所有事情並不好。使用 TweenAnimationBuilder 可以用更少的程式碼行實現相同的功能,而且不需要同時管理 AnimationController 和呼叫 setState。

class MyPragmaticWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return TweenAnimationBuilder(
tween: IntTween(begin: 0, end: 299792458),
duration: const Duration(seconds: 1),
builder: (BuildContext context, int i, Widget child) {
return Text('$i m/s');
},
);
}
}

總結

我們看到了計時器到底是什麼。我們看到了如何手動監聽 AnimationController。而且,我們看到了動畫在基本層面上只是快速連續地重新構建 Widget。您可以在任何幀上做任何您想做的事情。

本文是系列文章的一部分!請查看這裡的其他文章:


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

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

【文章翻譯】Announcing CodePen support for Flutter

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

宣布 CodePen 支援 Flutter

今天,我們很興奮地宣布,CodePen,數百萬前端開發人員和設計人員的首選社群開發環境,將加入 對 Flutter 的支援!對於 Web 開發人員來說,CodePen 長期以來一直是分享設計探索、新技術和想法的絕佳場所。現在,隨著 Flutter 的引入,CodePen 使新的受眾能夠學習、分享和推廣他們的創造力。

用 CodePen 共同創辦人之一 Alex Vazquez 的話來說:

“多年來,CodePen 一直是 Flutter 和 Dart 程式設計語言的忠實粉絲。我們非常興奮 Flutter 現在支援行動裝置 Web。Flutter 社群正在快速發展,這就是我們很興奮地為 Flutter 提供自訂 CodePen 編輯器的原因!Flutter 正式成為 CodePen 社群的一員。我們迫不及待想看看您在 CodePen 上使用 Flutter 建立什麼。”

從 Flutter 誕生之日起,我們就將其設計為創意表達的畫布。看到越來越多人認可其設計能力,這令人鼓舞,從我們在以設計為中心的 Flutter Interact 活動 上宣布與 Adobe 和 SuperNova 的合作,到 Fast Company 將 Flutter 列為本世紀最重要的設計理念之一。有了基於 CodePen 的 Flutter 環境的新增,創意專業人員現在可以利用 CodePen 來發現藝術性的 Flutter 靈感、建立令人印象深刻的 Flutter 作品集,並向全世界展示驚人的 Flutter 想法!

CodePen 的 Flutter 編輯器建立在與目前為 DartPad(由 Flutter 和 Dart 團隊建立的基於 Web 的編輯器,最近更新以支援 Flutter)提供動力的相同後端服務 dart-services 之上。當我們建立 DartPad 時,我們將其設計為一種教育工具,幫助開發人員學習 Flutter 和 Dart,並彼此分享程式碼片段。我們特別決定將 dart-services 開源,以便像 CodePen 這樣的網站可以將其適配到新的場景和受眾。

DartPad 是一個出色的工具,可以用於快速在程式碼中測試想法,或與其他開發人員分享您的片段,並且對於再現(和提交)錯誤很有用。您也可以在 CodePen 中執行此操作,但是 CodePen 的優勢在於它擁有活躍的設計社群,您可以在其中分享、評論、宣傳、嘗試設計想法,並從其他設計人員那裡獲得輸入。CodePen 的 Flutter 編輯器更像是您的「右腦」,您用它來為創意表達和設計靈感製作原型,而 DartPad 更像是您的「左腦」,當您需要快速測試想法或編寫技術概念時,就會使用它。

“光效”動畫Mariano Zorrilla 製作

CodePen 上的 Flutter 編輯器

讓我們快速瀏覽一下 CodePen 上的 Flutter 編輯器。您可以從 頭開始 或從現有的 範本 建立新的 Flutter 筆記(CodePen 對「程式碼片段」的稱呼)。非常感謝我們尊貴的 Flutter 社群成員(aednlaxerayushnishaddiegoveloperdivyanshub024egorbelibovgskinnerTeammkiisoftorestesgaolinSlaxXxX 等等)為範本列表貢獻了一些很酷的範例。

讓我們從 GooeyEdge 範本 開始。如您所見,Flutter 程式碼在左側,Flutter 的 Web 輸出在右側。您可以使用滑鼠拖動視覺邊緣來玩弄這個互動式設計。

“Gooey 邊緣”動畫Grant Skinner 製作

您也可以更改 Flutter 程式碼,並看到它們相應地生效。例如,如果我們將頁面控制指標的顏色從「白色」更改為「藍色」(第 326 行),您會看到顏色在幾秒鐘內更新!每次您進行更改時,CodePen 會自動重新編譯您的程式碼。只需更新一行,等待幾秒鐘,新的輸出就會出現。

頁面控制指標更改為藍色

現在讓我們看看如果我引入語法錯誤會發生什麼。假設我不小心刪除第 1 行末尾的分號。我將立即看到一條紅色橫條警告語法錯誤。這使您能夠輕鬆地發現和糾正錯誤。

引入語法錯誤時的警告訊息

社群功能

我們特別喜歡 CodePen 的社群功能。建立新筆記或發現社群建立的 Flutter「筆記」後,您可以儲存、收藏、新增到合集中、在社群媒體上分享,甚至分叉以建立自己的版本!

嘗試一下!

我們希望 CodePen 上的這個新的 Flutter 遊樂場能夠讓您建立和展示您的酷炫 Flutter 動畫、想法、小品等等。請您在 Twitter 上使用 #FlutterPen 與我們分享您的設計。我們迫不及待想看看您會建立什麼!如果您錯過了上週關於 免費培訓 的公告,您可能需要查看一下。請密切關注:我們很快就會有更多消息。


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