0%

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

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

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

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

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

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

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

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

案例研究:在 Dart 中實作 grep

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 檔案名稱:grep.dart
import 'dart:io';

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

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

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

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

grep(file, pattern);
}

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

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

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

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

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

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

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

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

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

什麼是 CPU 分析器?

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

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

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

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

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

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

執行緒中斷器

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

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

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

樣本收集

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

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

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

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

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

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

樣本處理

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

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

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

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

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

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

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

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 檔案名稱:grep.dart
import 'dart:developer';
import 'dart:io';

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

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

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

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

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

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

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

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

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

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 檔案名稱:grep.dart
import 'dart:developer';
import 'dart:io';

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

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

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

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

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

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

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

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

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

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

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

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

就這樣!

結語

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

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

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

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

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

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

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


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

undefined

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

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

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

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

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

使用 Flutter 建立的紙牌遊戲

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    vec3 rainbowPrime;

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

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

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

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

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

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

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

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

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

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

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

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

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

接下來是什麼

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

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


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

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

Flutter 為 iOS 開發的最新成就與未來方向

自 2017 年推出以來,Flutter 迅速成為 iOS 應用程式開發的首選。全球開發人員都 熱愛 Flutter。使用 Flutter,他們可以編寫一次程式碼,然後將其應用程式部署到 iOS、Android、網頁和桌面。這使得 Flutter 獲得了廣泛的追隨者,目前已有超過 一百萬個應用程式 使用 Flutter 構建。

對於重視創新設計的 iOS 開發人員而言,Flutter 已成為首選平台。透過提供空白畫布,Flutter 為設計師提供了無限的可能性。他們可以創造出擺脫預設設計系統的原始體驗。

今天,我想跟大家談談 Flutter 的一些最新改進,以及我們對 iOS 開發人員的未來關注領域。無論您是專業人士還是 Flutter 新手,請继续阅读以了解更多關於 Flutter 在 iOS 上的未來發展。

值得注意的 Flutter iOS 應用程式

Flutter 在 iOS 開發的各行各業和類別中都取得了成功。像 BMWSonosNubank 這樣的大型企業都使用 Flutter 來構建他們的數位體驗。

微信PUBG MOBILE 這樣的科技巨頭使用 Flutter 來支援超過十億活躍用戶。規模較小的公司也利用 Flutter 的快速開發週期。一個例子是 BrickIt,它使用機器學習來建議新的樂高積木創作。

蘋果和其他科技領導者甚至認可了 Flutter iOS 應用程式。Wonderous,Flutter 的參考應用程式,被 提名為 Webby 設計獎。蘋果將 Reflection.app 錄取到其著名的加速器計畫。So Vegan 多次 獲得蘋果的「今日 App」殊榮。

最新增強功能

Flutter 的 iOS 支援一直是我們的首要任務。我們在最近的版本中做出了重大改進。讓我們回顾一些亮點:

改進效能

Impeller,我們專為 Flutter 打造的渲染引擎,現在作爲 Flutter 在 iOS 上的預設引擎。Impeller 是我們團隊多年來一直努力的成果。它解決了 iOS 開發人員使用 Flutter 時最關心的問題:流暢的图形和頂級效能。自其發佈以來,我們看到了應用程式品質的顯著提高。最近,Flutter 引擎的改進進一步改善了啟動延遲,並減少了應用程式大小。

Impeller 還簡化了添加新功能的過程。這些功能從廣色域圖片支援到自訂渲染解決方案。我們在 Flutter Forward 上展示了其中一種自訂渲染解決方案,一個 3D 支援的概念驗證。

Impeller 使 Flutter 能够渲染 3D 圖形,如 Dash (我們的吉祥物) 的趣味演示所示。

開發人員體驗增強功能

我们了解构建和发布 iOS 应用程序的挑战。为了节省开发人员的时间,我们添加了新的工具和资源来简化整个过程。您现在可以通过 Wi-Fi 连接到 iOS 設備 来测试和调试您的应用程序。我们还 對發佈流程添加了驗證。此步骤确保您在将应用程序发布到 App Store 之前已完成所有必要步骤。

Enzo Conty 💚 on Twitter: “#Flutter now support iOS Wireless Debugging ! Here’s me trying it: pic.twitter.com/o7ziMasiYq / Twitter”

Flutter now support iOS Wireless Debugging ! Here’s me trying it: pic.twitter.com/o7ziMasiYq

此外,我们创建了以 iOS 为中心的文档和教程,以帮助开发人员学习 Flutter 并构建 iOS 应用程序。我们在文档中加入了 Swift 和 SwiftUI 示例。我们添加了从 Swift 切换到 DartSwiftUI 切换到 Flutter将 Flutter 添加到现有 iOS 应用程序 的指南。我们还添加了支持 iOS 上的風味 和使用 iOS 应用程序扩展(如主屏幕和鎖定屏幕小部件)的资源。

iOS 風格 UI 組件更新

Cupertino 函式庫 提供了类似 SwiftUI 和 UIKit 视图的 Widget(UI 組件)。这应该让您的应用程序在 Apple 设备上感觉宾至如归。为了更好地符合最新的 iOS 设计指南,我们对我們的 Cupertino 函式庫进行了重大更新。

我们已经解决了投票数最高的 issue,并添加了新的 Widget,如 CupertinoCheckboxCupertinoRadioCupertinoListTile 以及文本输入字段的拼写检查支持。

TextField 和 CupertinoTextField 上的自动 iOS 风格拼写检查

此外,我们认识到跨平台设计的 중요性。我们已為几个 Material Widget 添加了 自适应构造函数。这允许您的 Flutter 应用程序在 Android 和 iOS 设计约定之间进行调整。对于没有自适应构造函数的常见 Widget,我们创建了 初步指南,为您提供代码片段以调整整个 Widget 或 Widget 属性。

路線圖

随着我们继续努力让 Flutter 成为 iOS 开发人员的更好的工具,我们将重点关注以下几个关键领域:

与 Apple 生态系统的整合

Flutter 開發人員應該能夠輕鬆地在他們的應用程式中使用 Apple 的 API。这使他们能够创建融合所有 Apple 優秀功能的沉浸式体验。

几个月前,我们推出了 FFIgen。该工具生成绑定以 直接从 Dart 代码调用 Objective-C 和 Swift API。现在,一些应用程序正在使用 FFIgen 来调用 Apple 的 API。但是,仍然存在局限性。我们正在積極努力使该工具更加强大,使其支持异步回调和更好的 Swift 互操作性。

此外,我们知道 应用程序扩展 在 iOS 生态系统中的重要性。这就是为什么我们正在开发一种方法,使用 Flutter 来创建某些扩展的用户界面 (UI)。这将使开发人员能够重用他们 Flutter 应用程序中的组件来设计扩展的界面。值得注意的是,此方法并不适用于所有类型的扩展。例如,WidgetKit 小部件具有严格的 API 限制。但是,它将适用于其他常见扩展,例如 分享iMessage。若要了解我们的进展,您可以关注 Flutter Github issue 中的这项工作

Flutter 应用程序作为 iOS 分享扩展运行的概念验证

跨平台設計

在多个平台上部署您的应用程序需要一些设计方面的考虑。在自定义 UI 組件的设计以匹配品牌的同时,也要遵守平台约定之间取得平衡。

因此,我们正在研究如何改善设计和构建 Flutter 应用程序的体验。首先,我们希望让 Flutter 的开箱即用的 UI 組件更加灵活。这样,开发人员可以快速自定义 Widget 以匹配他们自己的设计系统。其次,我们希望利用更多的自动化适应性,使 Flutter 应用程序在 iOS 和 Android 上都感觉自然。

開發人員体验

我们致力于改善 Flutter 的 iOS 开发人员体验。一个关键的重点是缩短构建时间,从而提高开发人员的生产力。此外,我们还正在解决一些长期存在的请求,例如 重命名 Runner 应用程序 的功能。最后,我们将繼續優先考虑性能增强和通用的 iOS 保真度。

留下你的反馈

作为一个开源项目,您可以在 GitHub 上跟踪我们的进展。根据我们学到的知识以及您告诉我们的内容,我们可能会添加、延长或推迟功能。

与往常一样,我们重视您的反馈,并感谢您的支持!向我们发送意见的最佳方式是在 GitHub 上提交 issue,并填写我们的季度用户调查。您也可以在 Twitter上关注我,以了解 Flutter iOS 未来工作的最新信息.

资源


Flutter 在 iOS 开发中的未来 最初发布在 Flutter 上的 Medium,人们在那里通过突出显示和回应这个故事来继续讨论。

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

宣布 Flutter 3.10:無縫的網頁和行動整合、透過 Impeller 打破效能限制等等

歡迎來到 Flutter 3.10!我們迫不及待地想展示我們非凡的 Flutter 社群所付出的巨大努力。此 Flutter 版本運行著令人難以置信的 Dart 3,也於 今日 推出!

Flutter 3.10 包含許多針對網頁、行動、圖形、安全性等等的改進。讓我們開始吧!

架構

Material 3

Material 函式庫現在與最新的 Material Design 規範 相符。變更包括新的元件和元件主題、更新的元件視覺效果等等。開發人員必須使用 useMaterial3 主題標記來「選擇加入」這些變更。*在下一個穩定版本中,useMaterial3 預設為 true*。

若要選擇加入 Material 函式庫的 M3 版本,請在您的 MaterialApp 主題中設定 useMaterial3: true。當建立新的應用程式時,flutter create 命令會將此添加到您的主題中。

若要預覽這些變更,請查看 範例應用程式。此範例讓您可以切換 useMaterial3 的開關。

ColorScheme.fromImageProvider()

所有 M3 元件都會設定主題的 ColorScheme 的預設顏色。預設顏色方案使用紫色色調。您可以從單一「種子」顏色或從圖片建立自訂顏色方案。使用範例嘗試這兩種變體。產生的顏色方案看起來應該不錯,並且可以存取。

這提供了 BottomNavigationBar Widget 的 M3 版本。雖然 M3 使用不同的顏色、突出顯示和高度,但它與之前一樣運作。若要覆寫 NavigationBars Widget 的預設外觀,請使用 NavigationBarTheme Widget。雖然您不需要將現有應用程式遷移到此元件,但您應該在新的應用程式中使用它。

這提供了基於 Drawer Widget 的 M3 目的地選擇 Widget。NavigationDrawer 顯示一個由 NavigationDestinations Widget 組成的單一選擇列表。您也可以在此列表中包含其他 Widget。NavigationDrawer 在需要時可以捲軸。若要覆寫 NavigationDrawers Widget 的預設外觀,請使用 NavigationDrawerTheme Widget。

SearchBar 和 SearchAnchor

這些元件為搜尋查詢提供預測文字。當使用者輸入搜尋查詢時,應用程式會在「搜尋視圖」中計算出一個匹配回應列表。使用者可以選擇其中一個回應,或調整查詢。若要覆寫這些元件的 M3 設計,請使用 SearchBarThemeSearchAnchorTheme Widget。

次要標籤列

M3 允許您建立第二層標籤內容。若要區分此第二個 TabBar,請使用 TabBar.secondary

M3 更新的 DatePicker

M3 DatePicker 更新了 Widget 的行事曆和文字欄位版本的顏色、佈局和形狀。這不會改變 API,但會新增一個新的 DatePickerTheme

M3 更新的 TimePicker

M3 TimePickerDatePicker 一樣,更新了常規版本和緊湊版本的 Widget 的顏色、佈局和形狀。

M3 更新的 BottomSheet

除了 M3 顏色和形狀更新之外,底部表單現在還新增了一個可選的拖動手柄,當您將 showDragHandle 設定為 true 時會包含在內。

M3 更新的 ListTile

M3 ListTile 更新了此 Widget 的定位和間距。這包括內容填充、領先和尾隨 Widget 對齊、最小領先寬度和垂直間距。API 仍然保持不變。

為 M3 更新的 Drawer

M3 Drawer 更新了顏色和高度,同時進行了一些小的佈局變更。

TextField 更新

M3 更新所有 TextField Widget 以支援原生手勢。使用滑鼠雙擊或三擊與在觸控設備上雙擊或三擊的效果相同。預設情況下,TextFieldCupertinoTextField Widget 使用這些功能。

TextField 雙擊/點按手勢

  • 雙擊 + 拖動: 擴展文字區塊中的選取範圍。
  • 雙擊 + 拖動: 擴展文字區塊中的選取範圍。

TextField 三擊/점按手勢

三擊

  • 當在多行 TextField 中時,選取點擊位置的段落區塊(Android/Fuchsia/iOS/macOS/Windows)。
  • 當在多行 TextField 中時,選取點擊位置的行區塊(Linux)。
  • 在單行 TextField 中選取所有文字。

三擊

  • 當在多行 TextField 中時,選取點擊位置的段落區塊。
  • 在單行 TextField 中選取所有文字。

三擊 + 拖動

  • 擴展段落區塊中的選取範圍(Android/Fuchsia/iOS/macOS/Windows)。
  • 擴展行區塊中的選取範圍(Linux)。

Flutter 支援 SLSA 等級 1

Flutter 架構現在使用軟體工件的供應鏈等級(SLSA)等級 1 編譯。這表示已實作許多安全功能,包括:

  • 腳本化建置流程。Flutter 的建置腳本現在允許在可信賴的建置平台上自動化建置。在受保護的架構上建置有助於防止工件被篡改,從而提高供應鏈安全性。
  • 具有審計日誌的多方批准。Flutter 發佈工作流程只有在多位工程師批准後才會執行。所有執行都會建立可審計的日誌記錄。這些變更確保沒有人可以在原始程式碼和工件生成之間引入變更。
  • 出處。Beta 版本和穩定版本現在使用 出處 建置。這意味著可信賴的來源使用預期的內容建置了框架發佈工件。每個版本都會在 SDK 檔案 中發佈連結,供您查看和驗證出處。

這項工作還讓團隊能夠朝著 SLSA L2 和 L3 合規性邁進。這兩個等級專注於在建置流程期間和之後保護工件。

網頁

Flutter Web 應用程式改善載入時間

此版本會縮減圖示字體的檔案大小。它會從 Material 和 Cupertino 中修剪未使用的字形。

CanvasKit 縮減所有瀏覽器的尺寸

基於 Chromium 的瀏覽器可以使用更小的自訂 CanvasKit「風味」。託管的 CanvasKit 從 Google 的業界領先的 CDN 提供服務。這應該會進一步改善效能。

元素嵌入

您現在可以 從頁面中的特定元素提供 Flutter Web 應用程式。在此版本之前,您的應用程式可以填滿整個頁面主體或顯示在 iframe 標籤內。可以在 GitHub 中找到 範例程式碼

著色器支援

Web 應用程式可以使用 Flutter 的 片段著色器支援

引擎

Impeller

在 3.7 穩定版本中,我們在 iOS 上預覽了 Impeller。從那時起,我們收到了使用者們許多很棒的回饋,並加以解決。在此版本中,我們對 Impeller 進行了 250 多次提交,並將其設為 iOS 上的預設渲染器。預設情況下,使用 Flutter 3.10 為 iOS 建置的所有應用程式都會使用 Impeller。這些 iOS 應用程式將擁有更少的卡頓和更穩定的效能。

自 3.7 版本發佈以來,iOS 上的 Impeller 已改善其記憶體佔用空間。Impeller 使用更少的渲染傳遞和中間渲染目標。在較新的 iPhone 上,啟用有損紋理壓縮會減少記憶體佔用空間,而不會影響保真度。這些進展也讓 iPad 的效能顯著提升。

考慮到像 Wonderous 應用程式中的「拉取引言」螢幕這樣複雜的螢幕。這些改進共同將這些螢幕的記憶體佔用空間減少了近一半。記憶體使用量的減少也會使 GPU 和 CPU 負載略微下降。Wonderous 應用程式可能不會註冊到負載的下降。它的畫面在之前已經按預算渲染,但這個變更應該會延長電池壽命。

Impeller 還讓團隊能夠更快地交付熱門的功能請求。一個例子就是對 iOS 上更廣泛的 P3 色域的支援。請查看本文中的其他部分,了解此功能的描述。

社群貢獻加速了我們的進度,特別是 GitHub 使用者 ColdPaleLightluckysmg。他們撰寫了幾個與 Impeller 相關的修補程式,改進了保真度和效能。

雖然 Impeller 滿足了大多數 Flutter 應用程式的渲染需求,但您可以選擇不使用 Impeller。如果您選擇不使用,請考慮 在 GitHub 上提交議題 告訴我們原因。應用程式使用者可能會注意到 Skia 和 Impeller 的渲染有一些細微的差異。這些差異可能是錯誤,所以請不要猶豫提交議題。在未來的版本中,我們將移除 iOS 的傳統 Skia 渲染器,以減小 Flutter 的大小。

Impeller 的 Vulkan 後端仍在持續開發。Android 上的 Impeller 仍在積極開發中,但尚未準備好預覽。我們計劃在不久的將來分享更多相關資訊。

若要關注我們的進度,請查看 我們的 GitHub 專案看板

效能

除了 Impeller 之外,此版本還包含更多效能改進和修復。

消除卡頓

我們要感謝開源貢獻者 luckysmg。他們發現可以大幅減少從 Metal 驅動程式獲取下一個可繪製圖層的時間。若要獲得這個獎勵,您需要將 FlutterViews 的背景顏色設定為非空值。這個變更消除了最近的 iOS 120Hz 顯示器上的低畫面速率。在某些情況下,它會 將畫面速率提高三倍。這幫助我們解決了六個以上的 GitHub 議題。這個變更非常重要,我們將熱修復程式回溯到 3.7 版本。

在 3.7 穩定版本中,我們將載入本地圖片的動作從平台執行緒移到了 Dart 執行緒,以避免延遲來自平台執行緒的 vsync 事件。但是,使用者 注意到 Dart 執行緒上的這種額外工作也會導致一些卡頓。在此版本中,我們將打開和解碼本地圖片的動作從 Dart 執行緒移到了 背景執行緒。這個變更消除了在具有大量本地圖片的螢幕上出現潛在的長時間暫停,同時避免延遲 vsync 事件。在我們的本地測試和自動基線測試中,這個變更將多個同時圖片的載入時間縮短了一半。

我們繼續在 Flutter 的全新內部 DisplayList 結構基礎上建立優化。在此版本中,我們新增了一個 基於 R 樹的剔除 機制。這種機制會在我們渲染器中更早的階段移除繪製操作的處理。這種優化會加速 例如 輸出超出螢幕的自訂繪畫器。我們的 微基線測試 顯示 DisplayList 處理時間減少了最多 50%。具有剪裁自訂繪畫的應用程式可能會看到不同的改善。改善程度取決於隱藏的繪製操作的複雜性和數量。

減少 iOS 啟動延遲

在應用程式套件中查找識別碼的 低效策略 會增加應用程式啟動延遲。這種啟動延遲會與應用程式的大小成比例增加。在此版本中,我們修正了套件識別碼查找。這會將啟動延遲減少 100 毫秒,或者在大規模生產應用程式中減少約 30-50%。

減小尺寸

Flutter 使用 SkParagraph 作為文字整形、佈局和渲染的預設函式庫。我們包含了一個標記,用於回退到傳統的 libtxt 和 minikin 函式庫。由於我們對 SkParagraph 充滿信心,因此我們在 這個版本中移除 libtxt 和 minikin 以及它們的標記。這會將 Flutter 的壓縮尺寸減少 30KB。

穩定性

在 3.0 版本中,我們在渲染管線的後期啟用了一個 Android 功能。這個 Android 功能使用了進階的 GPU 驅動程式功能。這些驅動程式功能僅在一個「髒」區域發生變更時,就會重新繪製較少的螢幕。我們將其添加到我們圖形管線中具有類似效果的早期優化中。雖然我們的基線測試結果鼓舞人心,但出現了兩個問題。第一,改善最多的基線測試可能無法代表實際使用案例。第二,支援這個 GPU 驅動程式功能的設備和 Android 版本的集合被證明 很難找到。鑑於進展有限,我們 停用了 Android 上的部分重新繪製功能。

當使用 Skia 後端時,此功能會在 iOS 上保持啟用。我們期望在未來的版本中 在 Impeller 中啟用它

API 改進

APNG 解碼器

Flutter 3.10 處理了我們最 推廣的議題 之一。它 新增了解碼 APNG 圖片的能力。您可以使用 Flutter 現有的圖片載入 API 載入 APNG 圖片。

圖片載入 API 改進

Flutter 的工程總監 tvolkert 已為 dart:ui 的圖片載入 API 帶來了改進。我們新增了一個 新方法 instantiateImageCodecWithSize。這支援 使用案例,即載入符合以下三個條件的圖片:

  • 載入時間未知的寬高比
  • 邊界框約束
  • 原始寬高比約束

一個例子是,當應用程式嘗試從網路載入一組可能性中顯示一張圖片時。

行動裝置

iOS

無線除錯

您現在可以在沒有線索的情況下運行和熱重新載入 Flutter iOS 應用程式!在 Xcode 中成功地將您的 iOS 設備配對後,您可以使用 flutter run 將您的應用程式部署到該設備。如果您遇到問題,請驗證網路圖示是否出現在 Window > DevicesSimulators > Devices 下的設備旁邊。若要進一步了解,請查看我們的 文件

廣色域圖片支援

iOS 上的 Flutter 應用程式現在可以支援廣色域圖片的準確渲染。若要使用廣色域支援,應用程式必須使用 Impeller 並在 Info.plist 檔案中加入 FLTEnableWideGamut 標記。

拼寫檢查支援

SpellCheckConfiguration() Widget 現在預設支援 iOS 上的 Apple 拼寫檢查服務。若要使用此 Widget,請在 CupertinoTextField 中使用 spellCheckConfiguration 參數設定它。

自適應複選框和單選按鈕

此版本將 CupertinoCheckBoxCupertinoRadio Widget 添加到 Cupertino 函式庫中。它們會建立與 Apple 風格相符的複選框和單選按鈕元件。

Material 複選框和單選按鈕 Widget 添加了 .adaptive 建構子。在 iOS 和 macOS 上,這些建構子會使用相應的 Cupertino Widget。在其他平台上,它們會使用 Material Widget。

細化 Cupertino 動畫、轉場和顏色

Flutter 3.10 改進了一些動畫、轉場和顏色,使其與 SwiftUI 相符。這些改進包括:

PlatformView 效能

PlatformViews 出現在螢幕上時,Flutter 在 iOS 上 會限制重新整理速率 以減少卡頓。當應用程式顯示動畫或可捲軸的 PlatformViews 時,應用程式使用者會注意到這一點。

macOS 和 iOS 可以使用 Plugin 中的共用程式碼

Flutter 現在支援 pubspec.yaml 檔案中的 sharedDarwinSource 鍵,用於 Plugin。這個鍵表示 Flutter 應該共用 iOS 和 macOS 程式碼。

1
2
3
4
5
6
7
8
ios:
pluginClass: PathProviderPlugin
dartPluginClass: PathProviderFoundation
sharedDarwinSource: true
macos:
pluginClass: PathProviderPlugin
dartPluginClass: PathProviderFoundation
sharedDarwinSource: true

應用程式擴展的新資源

我們為 Flutter 開發人員添加了 文件,以使用 iOS 應用程式擴展。這些擴展包括實時活動、主螢幕 Widget 和共用擴展。

為了簡化建立主螢幕 Widget 和共用資料,我們在 path_provider 和 homescreen_widget Plugin 中添加了新方法。

跨平台設計的新資源

文件現在包含特定 UI 元件 的跨平台設計考量。若要進一步了解這些 UI 元件,請查看 Flutter UX GitHub 儲存庫中的討論。我們感謝您提供任何輸入或回饋!

Android

Android CameraX 支援

CameraX,一個 Jetpack 函式庫,簡化了在 Android 應用程式中添加豐富的相機功能的過程。此功能適用於各種 Android 相機硬體。在此版本中,我們為 Flutter Camera Plugin 添加了對 CameraX 的初步支援。此支援涵蓋以下使用案例:

  • 圖片捕捉
  • 影片錄製
  • 顯示實時相機預覽

若要嘗試一下,請選擇加入使用 CameraX 實作。若要選擇加入,請在您的 pubspec.yaml 檔案中添加以下行。

1
2
3
Dependencies:
camera: ^0.10.4 # Latest camera version
camera_android_camerax: ^0.5.0

當我們繼續添加更多 CameraX 功能,然後將 CameraX 設為預設實作時,我們非常希望收到您的回饋。

DevTools

我們將繼續改進 DevTools,這是 Dart 和 Flutter 的效能和除錯工具套件。以下是其中一些亮點:

  • DevTools UI 使用 Material 3。這既能使外觀現代化,又能增強可存取性。
  • DevTools 主控台支援在除錯模式下對正在運行的應用程式進行評估。在此版本之前,您只能在暫停應用程式時執行此操作。
  • 嵌入式 Perfetto 追蹤查看器 取代了先前的時間軸追蹤查看器。Perfetto 可以處理更大的資料集,並且比傳統追蹤查看器執行得更好。Perfetto 包含更多功能,例如:
    • 允許您固定感興趣的執行緒。
    • 點擊和拖動以從多個畫面中選取多個時間軸事件。
    • 使用 SQL 查詢從時間軸事件中提取特定資料。

若要進一步了解,請查看 DevTools 2.23.1DevTools 2.22.2DevTools 2.21.1 的發行備註。

棄用和重大變更

棄用的 API

此版本中的重大變更包括在 3.7 版本發佈後過期的棄用 API。若要查看所有受影響的 API,以及其他內容和遷移指南,請查看 此版本的棄用指南Dart Fix 可以解決其中許多問題,包括 IDE 中的快速修复和使用 dart fix 命令進行批量應用。

Android Studio Flamingo 升級

在您將 Android Studio 升級到 Flamingo 後,您可能會在嘗試運行 flutter runflutter build Flutter Android 應用程式時看到錯誤。此錯誤會發生,因為 Android Studio Flamingo 會將其捆綁的 Java SDK 從 11 更新到 17。早於 7.3 的 Gradle 版本 在使用 Java 17 時無法運行。我們 更新了 flutter analyze --suggestions 以驗證此錯誤是否由於您的 Java SDK 與 Gradle 版本之間不兼容而發生。

若要了解修復此錯誤的不同方法,請查看 我們的遷移指南

視窗單例棄用

此版本會棄用視窗單例。依賴它的應用程式和函式庫應該 從中遷移。這會為您的應用程式做好準備,以便在 Flutter 的未來版本中推出多視窗支援時可以運行。

感謝社群

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

總結

在我們總結此版本時,Google 的 Flutter 團隊要感謝所有使此版本成為可能的貢獻者。您的奉獻和辛勤工作幫助 Flutter 成為了業界的遊戲規則改變者,並繼續推動應用程式開發的革命。我們鼓勵您開始探索 Flutter 的最新穩定版本,以利用它提供的所有驚人功能。若要執行此操作,只需運行 flutter upgrade 即可!敬請關注未來 Flutter 的更多令人興奮的更新。


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

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

Flutter 3.10:圖形效能、網頁整合、開發人員生產力和安全性的大幅更新

A fun image of the Flutter mascot, Dash, standing by a racecar. She is holding a blue flag with “3.10” printed on it.

我們非常高興能回到今年的 Google I/O,從我們在加州山景城的總部附近向全世界進行直播!

就在三個月前,我們在肯亞奈洛比的 Flutter Forward 上推出了大膽的 Flutter 和 Dart 新路線圖。在 I/O 上,我們將透過四個主題的更新,分享我們對該願景的進展:突破性的圖形體驗、與網頁和平台的無縫整合、對新興架構的支持,以及對開發人員體驗的關注。

At Flutter Forward, we announced four areas of focus for the upcoming months: breakthrough graphics performance, seamless integration for web and mobile, early to new and emerging architectures, and continued focus on developer experience.

如您所知,Flutter 是一個 UI 工具包,讓應用程式開發人員可以從單一程式碼庫為行動裝置、網頁、桌面和嵌入式設備進行開發。Flutter 使您能夠建立 美麗 的應用程式,讓您完全控制螢幕上的每個像素。它 速度很快,利用裝置功能,支援硬體加速圖形和原生編譯的機器碼。它 富有成效,使用狀態熱重載等技術,讓您可以立即在應用程式中看到程式碼變更。它的 可移植性 使您能夠使用相同的原始碼部署到各種平台,而不會出現任何意外。此外,它是完全 開放原始碼 的,無需支付授權費用,也不需要為開發工具付費。

持續的 Flutter 動量

Flutter 的使用在 Google 和整個更廣泛的產業中持續增長。在 Google,團隊已在行動裝置、網頁和桌面平台上部署了 Flutter 應用程式,包括:

關於 Classroom,我們在 Flutter Forward 上宣佈,該團隊正在使用 Flutter 建立行動應用程式的新版本。新版本目前正在 iOS 上開始推出,最新的 Android 應用程式中也將很快推出新功能。這個版本的 Classroom 使用最新的 Flutter 技術,包括我們新的 Impeller 圖形渲染引擎,這可以實現快速、無卡頓的 UI。

使用 Flutter 重寫 Classroom 甚至提高了效能。應用程式的新版本比舊版本具有更快的冷啟動時間,正如這個並排影片所展示的:

我們將繼續投資於套件,以將您的 Flutter 應用程式與 Google 開發者生態系統的其餘部分連接。這包括對我們的 原生廣告的 Google Ads 支援進行重大更新針對 Firebase 的新 Windows 支援以及更好的網頁支援;以及對 更深入的 Android 互操作性 的新的實驗性支援。

在六個平台(Android、iOS、網頁、Windows、macOS 和 Linux)上都有 Flutter 支援,現在有超過一百萬個已發佈的應用程式使用 Flutter。它們來自世界各地:從 SNCF Connect,法國鐵路的火車旅行應用程式;到 Apple 當天應用程式獲獎者 SO VEGAN;從 Rive 的快速桌面應用程式,用於建立動畫圖形到關係賦能的 Agapé 行動和平板電腦應用程式;從美麗的 全新設計的 Global Citizen 應用程式新的 Ubuntu Linux 安裝程式,我們很高興看到 Flutter 價值的證明!

使用 Impeller 實現突破性的圖形效能

我們希望 Flutter 能夠為開發人員和設計師提供巨大的力量,以提供出色的圖形體驗。在過去的幾年中,我們一直在重建我們的圖形渲染架構,以實現速度和功能。我們將這個新引擎稱為 Impeller

“我們希望 Flutter 能夠為開發人員和設計師提供巨大的力量,以提供出色的圖形體驗。”

自從我們在 iOS 上推出 Impeller 以後,我們一直在擴展測試和我們的早期採用者合作,以驗證生產品質並進一步調整效能。在今天的 Flutter 3.10 版本中,我們很高興地宣布 Impeller 現在已為 iOS 預設啟用,只需遷移到最新版本的 Flutter,應用程式就可以獲得很大的效能提升。

隨著 Impeller 現在已啟用生產 iOS 使用,我們將注意力轉向為 Android 添加預覽支援。正如 iOS 上的 Impeller 使用底層 Metal API 一樣,Android 上的 Impeller 實作建立在 Vulkan 之上,該 API 為底層圖形硬體上的快速渲染提供了低階 API。雖然絕大多數 現代 Android 裝置 支援 Vulkan,但我們將支援舊裝置的向後相容模式。我們將在接下來的部落格文章中,分享 Impeller for Android 的早期預覽,以及更多有關 Impeller 技術基礎的詳細資訊。

與網頁的無縫整合

正如我們在 Flutter Forward 上所述,我們的目標與大多數現有的網頁框架不同。我們為網頁構建的 Flutter 實作明確 不是 旨在成為通用的網頁框架。現有的網頁框架,例如 Angular 和 React,已經有很多,它們在這個領域做得很好。相反,Flutter 是第一個在架構上圍繞新興的 Web 技術(例如 CanvasKitWebAssembly)進行設計的框架,這些技術特別適合複雜的應用程式體驗。

“Flutter 是第一個在架構上圍繞新興的 Web 技術(例如 CanvasKit 和 WebAssembly)進行設計的框架。”

自從我們早期發佈 Flutter 的網頁支援以來,我們一直在努力改進效能、無障礙性和互操作性。

感知效能 的一個主要因素是 載入時間,即從請求頁面到頁面變成互動式的時間間隔。在此版本中,我們取得了重大進展,這得益於 CanvasKit 在所有瀏覽器上的大小減少,以及針對基於 Chromium 的瀏覽器的額外最佳化。在 Flutter 3.10 中,CanvasKit 縮小到 1.5MB(我們之前的版本為 2.7MB)。圖示字體也已修剪掉未使用的字形,在多數情況下尺寸減少了 100 倍。由於這些最佳化,我們使用模擬的電纜連線將預設計數器應用程式的載入時間縮短了 42%。

正如在 Flutter Forward 上預覽的那樣,我們現在 支援將 Flutter 內容嵌入到現有的 HTML 網頁中,而不是讓 Flutter 佔據整個頁面或需要使用內聯框架。在 Flutter 3.10 中,我們引入了 元素嵌入,它讓您可以像在頁面上添加任何其他 CSS 元素一樣整合 Flutter 內容 - 例如,應用複雜的 CSS 轉場和轉換。若要開始使用,請嘗試這些使用 JavaScript 的範例,或者將 Flutter 包含在 Angular 組件 中。

繼續專注於突破性的圖形效能,Flutter 3.10 還獲得了對網頁上 片段著色器 的支援。自訂著色器可使用於提供比 Flutter SDK 提供的更豐富的圖形效果。著色器是一個用類似 Dart 的語言(稱為 GLSL)撰寫,並在使用者 GPU 上執行的程式。如果您想進一步了解,請查看 我們關於著色器的文件,以及我們的 新程式碼實驗室

對 WebAssembly 的新興架構提供早期支持

WebAssembly(通常縮寫為 Wasm)已逐漸成熟,成為現代瀏覽器的 現代瀏覽器 的平台中立二進制指令格式。在網頁上,Flutter 一直使用 Wasm 來分發 CanvasKit 執行時間,而 Dart 框架和應用程式程式碼歷史上一直編譯為 JavaScript。我們對針對 Wasm 而不是 JavaScript 感興趣已經有一段時間了。然而,直到最近,Wasm 缺乏對 Dart 等垃圾回收語言的原生支援。

因此,在過去的一年中,我們與 WebAssembly 生態系統中的幾個團隊合作,為標準引入了垃圾回收。這已透過一個名為 WasmGC 的新擴展來實現,該擴展目前在基於 Chromium 的瀏覽器和 Firefox 瀏覽器中具有接近穩定的實作。

WebAssembly 讓我們對將原生程式碼的效能帶到網頁的可能性感到興奮。Dart 的 JavaScript 編譯器(在 Google 的數百萬行程式碼中使用)已經生成了快速、經過優化調整的 JavaScript。但是,切換到 Wasm 將為我們提供原生程式碼的效率和 JavaScript 的可移植性,這將進一步提高我們在網頁上的效能。在一些早期的基準測試中,我們發現執行速度提高了 3 倍,這意味著可以提供更豐富的基於網頁的體驗。Wasm 將這與更輕鬆地整合其他語言(如 Kotlin 和 C++)撰寫的程式碼結合在一起。

“WebAssembly 讓我們對將原生程式碼的效能帶到網頁的可能性感到興奮。”

隨著我們等待瀏覽器支援變得更加普及,我們在預發佈通道中引入了將 Flutter 應用程式編譯為 WebAssembly 的預覽支援。我們希望您嘗試為自己的應用程式使用它,並為我們提供早期的回饋。若要進一步了解,請查看 flutter.dev/wasm

持續關注開發人員體驗

儘管我們希望透過之前提到的突破性的圖形效能和更豐富的網頁支援來取悅許多人,但我們在此版本中還引入了許多針對開發者速度和生產力的改進。而 我們詳細的技術部落格記錄了 Flutter 本身的數百項改進,這對現有的 Flutter 開發人員非常感興趣。

但或許此版本中對核心開發者體驗最重大的改進是 **發佈了 Dart 3 **,它包含在 Flutter 3.10 中。

Dart 3 完成了將健全的空安全帶到 Dart 生態系統的漫長旅程。撰寫空安全程式碼可以防止一整類來自在沒有檢查的情況下使用未初始化值的程式設計錯誤。雖然我們自 Dart 2.0 以來一直支援空安全程式碼,但我們現在已關閉了舊的「不安全」模式。作為一個生態系統,我們一直在為此做準備,並且由於排名前 1,000 個套件中的 99% 支援空安全,我們相信現在是進行轉換的適當時間。

“Dart 3 完成了將健全的空安全帶到 Dart 生態系統的漫長旅程。”

Dart 3 引入了許多其他新功能,包括記錄、模式和類別修飾符,這將提高 Dart 程式碼的可讀性和流暢性。前往 主要的 Dart 3 部落格 以獲取更多資訊和範例。Flutter 本身已經開始利用這些新的 Dart 3 功能,因此您將看到我們自己的程式碼庫隨著這些功能的推出而有所改進。我們相信您會喜歡在自己的程式碼中使用它們。

SLSA 和軟體供應鏈安全性

在現代,保護關鍵軟體基礎架構免受威脅不幸地成為一種必要性。因此,除了之前提到的主要功能之外,我們的工程團隊今年 優先考慮了對安全性的投資。這種投資涵蓋安全性測試、自動化和供應鏈安全性。

“我們的團隊優先考慮了對安全性的投資”

透過進行以下工作,我們旨在讓企業對採用 Flutter 更有信心:

  • 開放原始碼安全基金會 最佳實務程式 作為一個有用的基準,幫助專案遵循安全性和漏洞管理的最佳實務。我們很高興地宣布,我們已完成 該程式通過級別的 100% 通過要求。我們將繼續推進,以證明符合 銀色金色 標準。
  • Flutter 也已在所有 Flutter 關鍵儲存庫上啟用了 OpenSSF ScorecardsDependabot。OpenSSF Scorecards 是一個靜態分析工具,檢查您的儲存庫是否符合最佳實務,並且在未遵循這些實務時建立議題。Dependabot 監控專案相依關係中的漏洞,並在必要時建立拉取請求以更新它們。使用這些工具,Flutter 團隊已在我們的網站和程式碼實驗室中識別並解決了超過 300 個漏洞。
  • Flutter 和 Dart SDK 以及這些 SDK 的發佈工作流程最近達到了 SLSA L1 級別。SLSA(軟體工件供應鏈級別)框架幫助開放原始碼專案維持強大的供應鏈安全性。達到 SLSA L1 是保護 Flutter 開發人員每天使用的工具的一個重要步驟。
  • 最後,我們對我們的基礎架構進行了許多安全性改進。這包括遷移到更安全的構建和測試環境,同時限制對這些環境的存取。此外,我們改進了 Flutter 框架和引擎工件的記錄和審計功能,提供對工件的出色保護。這些改進讓 Flutter 團隊對我們在構建過程中產生的工件如何處理有了更深入的了解。

開放原始碼專案,成千上萬人的作品

此版本中有成千上萬的其他變更,我們希望這些變更能讓現有的 Flutter 開發人員感到滿意。但值得注意的是,這些貢獻中有多少來自 Google 之外的開發人員。它們包括新功能;文件改進;將 Flutter 擴展到我們從未料想過的範圍的套件;以及可重現的議題報告和功能請求,這些請求為我們提供了關於如何改進的新觀點。

Flutter 不是一個 Google 專案,而是一個 我們所有人 的專案。我們非常感謝社群的多元化和熱情,這讓 Flutter 成為它現在的樣子。加入您參與這項使命是件令人愉快的事;Flutter 的未來將比以往任何時候都更加光明!


在 I/O 2023 上與 Flutter 和 Dart 一起向前邁進 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

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

根據您的回饋新增的 GMA Flutter 功能!

當您建立和發展 Flutter 應用程式時,您可能希望您的應用程式賺錢。您可以透過多種方式為應用程式獲利。引入廣告將是最常見的策略之一。若要引入廣告,您可以使用 Google 行動廣告 (GMA) Flutter Plugin。GMA Plugin 可以將來自 AdMob、Google Ad Manager 或 AdSense 的廣告放置在您的 Flutter 行動應用程式中。

功能和工具請求隨著 GMA Flutter Plugin 使用量的增加而增長。根據您對 Ad Manager 和 AdMob 的調查回覆,Plugin 現在支援:

  • Dart 對原生廣告的支援
  • 更新的應用程式範例
  • 網頁檢視 API 適用於廣告

Dart 對原生廣告的支援

使用原生廣告,您可以自訂出現在應用程式中的廣告的外觀和風格。您可以從頭開始設計廣告。設定廣告的外觀、放置位置以及它們在您現有應用程式設計中的工作方式。

原生廣告可以與周圍內容的佈局相符。這會帶來更連貫、更自然的應用程式體驗。您可以透過多種方式實作這些廣告。將它們加入到您的動態消息、轉場螢幕和覆蓋層中,或將它們加入到您應用程式中您想要顯示廣告的任何位置。

之前,實作原生廣告需要撰寫平台程式碼。我們同意這是一種次佳的開發人員體驗,並且希望您盡可能地使用 Dart。

我們加入了 Dart API 以讓您能夠使用 Dart 自訂原生廣告。這些 API 利用 GMA Dart 中的原生範本 來加速廣告體驗的建立。新的 Dart API 並不替換平台程式碼。如果您想要更精細地控制平台特定的廣告自訂,您可以繼續撰寫平台程式碼。

若要進一步了解使用原生廣告體驗的最佳實務,請查看我們的 原生廣告指南

若要開始實作這些廣告,請查看我們的 開發人員文件

更新的應用程式範例

當您在 GMA Flutter Plugin 上提交問題時,如果您可以包含一個最小的可重現應用程式範例來調查該問題,將會有幫助。也就是說,我們聽到了您的回饋,您可能難以產生或分享可重現的範例。

為了簡化疑難排解問題,我們將大型範例拆分為較小的範例。我們使用 每個廣告格式和中介的範例 替換了我們的單體應用程式範例。現在當您提交問題時,您可以使用這些應用程式。這些應用程式允許您分享最小的可重現範例。這在分享您自己的應用程式範例會很繁瑣或不可能時很有幫助。

我們希望這些較小的個別應用程式範例能夠簡化對新格式或中介的測試。每個範例都包含一個自述檔案,作為我們的 GMA 開發人員文件的補充(Ad ManagerAdMob)。

每個範例應用程式都實作了兩個重要的功能:

  1. AndroidiOS 的 Google 測試廣告
  2. 測試應用程式 ID

在應用程式範例中測試您自己的廣告單元的功能時,請將廣告單元 ID 和應用程式 ID 替換為您自己的 ID。請確保您已 啟用測試廣告。如果您在未處於測試模式的情況下點擊太多廣告,您可能會導致您的帳戶被標記為無效活動。

旗幟應用程式範例 來說明:

  1. 將以下測試廣告單元 ID 替換為您自己的 ID:
1
2
3
final String _adUnitId = Platform.isAndroid
? 'ca-app-pub-3940256099942544/6300978111'
: 'ca-app-pub-3940256099942544/2934735716';
  1. 將以下測試應用程式 ID 替換為您自己的 ID,位於 AndroidiOS 設定檔案中。

這就是開始測試您的功能所需的所有步驟。

若要查看所有範例,請查看 googleads-mobile-flutter/samples

網頁檢視 API 適用於廣告

我們了解一些開發人員更喜歡在他們的行動應用程式中使用 WebViews。這使他們可以避免重新標記廣告庫的需要。如我們的 AdSense 政策Ad Manager 政策 中所述,我們只支援在某些 WebView 技術中為網頁內容獲利。

為了支援 Ad Manager 和 AdSense 對行動應用程式 WebViews 的獲利,我們正在推出適用於廣告的 GMA 網頁檢視 API 的 Flutter 支援。如果您的應用程式使用 WebViews 來顯示從 Ad Manager 或 AdSense 提供廣告的網頁內容,您必須使用適用於廣告的 WebView API。

  1. 如果您不需要從應用程式內網頁檢視中請求廣告,請使用 GMA Flutter Plugin 實作廣告格式(Ad Manager 指南AdMob 指南)。
  2. 如果您從應用程式內網頁檢視中請求廣告,請使用適用於廣告的 WebView API 來註冊 WebView,以遵守我們的 AdSense 政策Ad Manager 政策

您可以在同一個應用程式中執行任一選項,甚至同時執行兩個選項。

注意:AdSense 程式碼Google 發佈者標籤 中的 JavaScript 會建立並發送廣告請求。您不需要使用 SDK 提出任何廣告請求。只有行動網站和桌面網站的 庫存格式 可以與此 API 一起使用。

若要開始實作,請查看我們的 開發人員文件

我們如何決定接下來要建立什麼?

您的回饋會影響我們的產品路線圖。

自 2020 年公開測試版以來,我們一直在 GitHub 與您合作,解決問題並收集功能請求。GMA Flutter Plugin 的採用已顯著增長。我們希望擴展我們的支援以滿足更高的需求。

從 2023 年第三季開始,如果您透過 GitHub Issues 報告錯誤或功能請求,我們建議您訪問我們的 開發人員論壇 來解決您的問題。開發人員論壇是您最有效的技術支援選項。我們安排了技術支援團隊為論壇提供服務。使用 GMA SDK 的開發人員也會參與論壇。如果您對 Flutter GMA SDK 有任何問題,論壇應該可以提供最快速和最準確的回覆。我們正在將論壇中對 Flutter 的支援擴展到支援 Android、iOS 和 Unity GMA Plugin 的相同團隊。

我們歡迎 Flutter 開發人員加入 Google 行動廣告社群。我們邀請您嘗試 GMA SDK,並在論壇中與您的應用程式發佈商同仁交流!


2023 年 Google 行動廣告針對 Flutter 的更新 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

宣布 Dart 3

來自 Google I/O 2023 的問候。今天,我們將從山景城現場宣布 Dart 3——迄今為止最大的 Dart 版本!Dart 3 包含三個主要進展。首先,我們完成了通往 100% 聲稱空安全性的旅程。其次,我們為記錄、模式和類修飾符添加了主要的新語言功能。第三,我們正在預覽未來,在未來我們將透過 Wasm 編譯為 Web 提供原生程式碼,從而擴展我們的平台支援。讓我們深入了解細節。

100% 聲稱空安全性

在過去的四年中,我們將 Dart 演變為一種快速、可移植且現代的語言。現在,有了 Dart 3,它成為了 100% 聲稱空安全的語言!正如我們之前所 討論過的那樣,我們認為沒有其他程式設計語言曾經為現有的語言添加過聲稱空安全性。所以,這是一段漫長的旅程。

有了 Dart 中的 100% 空安全性,我們擁有一個 聲稱 的類型系統。您可以相信,如果類型表示值不為 null,那麼它永遠不會為 null。這避免了某些類型的編碼錯誤,例如空指標異常。它還允許我們的編譯器和運行時以在沒有空安全性時無法實現的方式優化程式碼。這個設計選擇涉及一個權衡。儘管遷移變得有點困難,但我們相信我們為 Dart 做出了正確的選擇。

遷移到 Dart 3

在實現聲稱空安全性方面,Dart 社群的不懈支援至關重要:pub.dev 上排名前 1000 的套件中有 99% 支援空安全性!

基於此,我們預計絕大多數已遷移到空安全性的套件和應用程式都可以在 Dart 3 中正常運行。在極少數情況下,Dart 3 中的少量相關清除可能會影響某些程式碼。一些遺留核心函式庫 API 已被移除(#34233#49529),並且一些工具已調整(#50707)。如果您在遷移到使用 Dart 3 SDK 時遇到任何問題,請參閱 Dart 3 遷移指南。除此之外,我們希望您能享受新的合理化核心函式庫和工具。

主要語言功能 - 記錄、模式和類修飾符

Dart 3 不僅僅是關於更改現有語言。它還包括添加重要的新功能和能力!這些包括記錄、模式和類修飾符。

使用記錄構建結構化資料

傳統上,Dart 函數只能返回單個值。因此,需要返回多個值的函數必須將這些值打包到其他資料類型中,例如映射或列表,或者定義可以保存這些值的新的類別。使用未類型化的資料結構會削弱類型安全性。必須定義新的類別僅用於攜帶資料,這在編碼過程中會增加摩擦。您已經很清楚地告訴我們:對多個返回值的語言請求是我們 排名第四 的問題。

使用記錄,您可以使用乾淨利落的語法構建結構化資料。考慮這個函數。它讀取 JSON blob 的名稱和年齡,並將它們都返回到一個記錄中:

1
2
3
(String, int) userInfo(Map<String, dynamic> json) {
return (json['name'] as String, json['height'] as int);
}

這對所有 Dart 開發人員來說應該很熟悉。記錄看起來像列表常量,例如 ['Michael', 'Product Manager' ],但使用括号而不是方括号。在 Dart 中,記錄是一個通用功能。它們可以用於函數返回值以外的其他用途。您也可以將它們存儲在變數中,將它們放入列表中,將它們用作映射中的鍵,或建立包含其他記錄的記錄。您可以添加未命名的欄位,就像我們在之前的示例中所做的那樣,以及命名的欄位,例如 (42, description: 'Meaning of life')

記錄是值類型,沒有身份。這使得我們的編譯器可以在某些情況下完全擦除記錄物件。記錄還帶有自動定義的 == 運算符和 hashCode 函數。記錄文件 具有更多詳細信息。

使用模式和模式匹配處理結構化資料

記錄簡化了您構建結構化資料的方式。這不會取代使用類別來構建更正式的類型層次結構。它只提供另一個選項。無論哪種情況,您可能都希望將這些結構化資料分解成其個別元素以進行處理。這就是模式匹配發揮作用的地方。

考慮一種基本的模式形式。以下記錄模式將記錄分解成兩個新的變數 nameheight。這些變數之後可以像任何其他變數一樣使用,例如在呼叫 print 時:

1
2
var (String name, int height) = userInfo({'name': 'Michael', 'height': 180});
print('User $name is $height cm tall.');

列表和映射也存在類似的模式。對於所有這些模式,您可以使用下劃線模式來跳過個別元素:

1
var (String name, _) = userInfo(...);

模式在 switch 語句中使用時效果顯著。Dart 從一開始就對 switch 提供了有限的支援。在 Dart 3 中,我們擴展了 switch 語句的功能和表現力。我們現在在這些情況下支援模式匹配。我們已經不需要在每個 case 的末尾添加 break。我們還支援邏輯運算符來組合 case。以下示例展示了一個乾淨利落的 switch 語句,它解析字元碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
switch (charCode) {
case slash when nextCharCode == slash:
skipComment();

case slash || star || plus || minus:
operator(charCode);

case >= digit0 && <= digit9:
number();

default:
invalid();
}

當您需要為每個 case 使用一個或多個語句時,switch 語句提供了很好的幫助。在某些情況下,您只想做的是計算一個值。對於這種情況,我們提供了一個非常簡潔的 switch 表達式。這類似於 switch 語句,但使用不同的語法,該語法針對表達式進行了微調。以下示例函數返回一個 switch 表達式的值,以計算當前日期的星期幾描述:

1
2
3
4
5
6
String describeDate(DateTime dt) => 
switch (dt.weekday) {
1 => 'Feeling the Monday blues?',
6 || 7 => 'Enjoy the weekend!',
_ => 'Hang in there.'
};

模式的一個強大功能是能夠檢查「窮舉性」。此功能確保 switch 處理所有可能的情況。在前面的示例中,我們正在處理 weekday 的所有可能值,weekday 是一個 int。我們透過組合針對特定值 1、6 或 7 的匹配語句,然後使用預設情況 _ 來處理剩餘情況,來窮舉所有可能的值。若要啟用對使用者定義的資料層次結構(例如類別層次結構)的這種檢查,請在類別層次結構頂部使用新的 sealed 修飾符,如以下示例所示:

1
2
3
4
5
6
7
sealed class Animal { ... }
class Cow extends Animal { ... }
class Sheep extends Animal { ... }
class Pig extends Animal { ... }

String whatDoesItSay(Animal a) =>
switch (a) { Cow c => '$c says moo', Sheep s => '$s says baa' };

這將返回以下錯誤,提醒我們漏掉了對最後一個可能的子類型 Pig 的處理:

1
2
line 6 • The type 'Animal' is not exhaustively matched by the switch cases
since it doesn't match 'Pig()'.

最後,if 語句也可以使用模式。在下面的示例中,我們正在使用 if-case 匹配一個映射模式來分解 JSON 映射。在其中,我們根據常量值(字串,例如 'name''Michael')和類型測試模式 int h 進行匹配,以讀取 JSON 值。如果模式匹配失敗,Dart 將執行 else 語句。

1
2
3
4
5
6
7
8
final json = {'name': 'Michael', 'height': 180};

// Find Michael's height.
if (json case {'name': 'Michael', 'height': int h}) {
print('Michael is $h cm tall.');
} else {
print('Error: json contains no height info for Michael!');
}

這僅觸及了您可以使用模式完成的所有事情。我們相信它們將在所有 Dart 程式碼中變得普遍。若要進一步了解,請查看 模式文件模式程式碼實驗室

使用類修飾符為類別提供細粒度的存取控制

第三個 Dart 3 語言功能是類修飾符。與我們期望每個 Dart 開發人員使用的記錄和模式不同,這更像是一個強大的使用者功能。它滿足了編寫大型 API 表面或構建企業級應用程式的 Dart 開發人員的需求。

類修飾符使 API 作者能夠僅支援特定功能集。不過,預設值保持不變。我們希望 Dart 保持簡單易懂。因此,和以前一樣,常規類別可以 構造擴展實作,如下面的示例所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Vehicle {
String make; String model;
void moveForward(int meters) { ... }
}

// Construct.
var myCar = Vehicle(make: 'Ford', model: 'T',);

// Extend.
class Car extends Vehicle {
int passengers;
}

// Implement.
class MockVehicle implements Vehicle {
@override void moveForward ...
}

類修飾符支援對此添加限制。考慮一些示例用例:

  • 使用介面類別,您可以為其他人定義一個要實作的合約。介面類別不能被擴展。
  • 使用基類別,您可以確保您類別的所有子類型都繼承自它,而不是實作其介面。這確保私有方法在所有實例上都可用。
  • 使用 final 類別,您可以關閉類型層次結構,防止在您自己的函式庫之外建立任何子類別。作為一個示例優點,這允許 API 擁有者添加新的成員,而不會冒著對 API 使用者造成重大變更的風險。

有關詳細信息,請參閱新的 類修飾符文件

未來展望

Dart 3 不僅僅是您今天可以使用的功能方面的一個重大進步。我們還將預覽一下下一步。

Dart 語言

記錄、模式和類修飾符是十分重要的新功能,因此,它們的設計部分可能存在可以改進的地方。我們將繼續監控 您的回饋,並查看是否需要在 Dart 3 後的次要版本中進行更新。

我們還正在考慮一些更小、更增量式的功能,這些功能完全不可破壞,並且專注於提高開發人員的生產力,而無需遷移成本。我們正在探索的兩個示例是 內聯類別,用於以零成本的「包裝器」包裝現有類型,以及 主建構函數;此功能引入了定義包含少數欄位和主建構函數的類別的更簡潔的語法。

我們之前討論過宏(也稱為 元程式設計)。我們特別關注這一點,以實現對 JSON(和類似資料)的更好的反序列化,以及啟用資料類別。考慮到元程式設計的規模和固有的風險,我們正在採取非常徹底的方法,因此我們沒有任何明確的時間表可以分享,即使是最終確定設計決定的時間表。

原生互操作

行動裝置和桌面的應用程式通常依賴於原生平台提供的許多 API,無論是通知、支付還是獲取手機的位置。傳統上,在 Flutter 中,這些 API 是透過構建外掛來存取的,這需要同時編寫 API 的 Dart 程式碼和大量平台特定程式碼以提供實作。

我們已經支援與編譯成 C 函式庫的程式碼進行互操作,可以使用 dart:ffi。我們目前正在努力擴展此支援,以支援 Android 上的 Java 和 Kotlin 互操作 以及 iOS/macOS 上的 Objective C 和 Swift 互操作。若要了解 Android 互操作的簡介,請查看新的 Google I/O 23 Android 互操作性影片

編譯成 WebAssembly - 使用原生程式碼定位 Web

WebAssembly(簡稱為 Wasm)作為一種平台中立的二進制指令格式,在 所有現代瀏覽器 中日益成熟。Flutter 架構已經使用 Wasm 一段時間了。這是我們將以 C++ 編寫的 SKIA 圖形渲染引擎透過 Wasm 編譯的模組傳遞到瀏覽器的方式。我們一直對使用 Wasm 部署 Dart 程式碼很感興趣,但我們一直受阻。Dart 與許多其他面向物件語言一樣,使用垃圾回收。在過去的一年中,我們已與 Wasm 生態系統中的多個團隊合作,為 WebAssembly 標準添加新的 WasmGC 功能。這現在在 Chromium 和 Firefox 瀏覽器中接近穩定狀態。

我們在將 Dart 編譯成 Wasm 模組方面的工作具有兩個針對 Web 應用程式的目標:

  • 載入時間:我們希望我們能夠使用瀏覽器載入速度更快的 Wasm 傳遞部署有效載荷,從而縮短到達使用者可以與 Web 應用程式互動的點所需的時間。
  • 效能:由 JavaScript 驅動的 Web 應用程式需要即時編譯才能實現良好的效能。Wasm 模組是更低階的,更接近機器碼,因此我們認為它們可以透過更少的卡頓和更一致的畫面速率提供更高的效能。
  • 語義一致性:Dart 對於我們支援的平台之間的高度一致性感到自豪。但是,在 Web 上,有一些例外。例如,Dart Web 目前在 數字的表示方式 方面有所不同。有了 Wasm 模組,我們將能夠將 Web 視為一個「原生」平台,其語義類似於其他原生目標。

我們很興奮地宣布今天首次預覽將 Dart 編譯成 Wasm!我們最初的重點是 Flutter Web 支援。這還處於早期階段,我們還有很多工作要做,但我們 邀請您進行嘗試,看看您是否像我們一樣興奮。

結語

感謝您耐心閱讀到結尾。我們希望這篇文章讓您對 Dart 3 感到興奮,Dart 3 目前已在獨立的 Dart SDKFlutter 3.10 SDK 中提供。

我們已使用聲稱空安全性、核心函式庫和工具清理對 Dart 語言進行了重大改進。有一些主要的新語言功能,使 Dart 透過記錄和模式變得更具表現力和簡潔性。對於大型 API 表面,類修飾符啟用了詳細的控制。我們還包括對 WebAssembly 的支援預覽。

有了所有這些功能,我們認為 Dart 3 展示了我們的長期願景:為在任何平台上構建快速應用程式構建最具生產力的程式設計語言。我們希望您也這麼認為!


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

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

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

Wonderous 榮獲 Webby 獎提名

Wonderous 在最佳使用者介面類別中獲得 Webby 獎提名。

您可以在 Webby 獎網站 上為 Wonderous 投票。

我們在 8 月份與 gSkinner 團隊合作推出 Wonderous ——一個展示 Flutter 優勢的行動應用程式。自推出以來,已有超過 3 萬名使用者安裝了 Wonderous。使用者在 App Store 上給予它 5 星評分。

Wonderous 在第 27 屆年度 Webby 獎中獲得了應用程式、dApps 和軟體類別的最佳使用者介面提名。紐約時報將 Webby 獎譽為「網際網路的最高榮譽」。國際數位藝術與科學學院 (IADAS) 是表彰網際網路卓越成就的領先國際獎項組織,每年頒發 Webby 獎。

“提名者正在為網際網路上的創新和創意樹立標竿,”Webby 獎主席克萊爾·格雷夫斯說。“在我們今年收到的近 14,000 件作品中脫穎而出,這是一項非凡的成就。”

Wonderous 擁有精美的圖形和獨特的動畫,讓您可以直接從設備中體驗世界奇觀。Wonderous 使用 Flutter 建立一個富有表現力的使用者介面,適合行動裝置的尺寸,並且不符合標準設計系統。相反,它實現了設計師的創意願景。

您可以從 App StorePlay Store 下載 Wonderous。您可以在 這裡 了解自適應設計、動畫、效能和無障礙方面的最佳實務,也可以查看 完整的原始碼

作為提名人,Wonderous 也可能贏得 Webby 人氣獎。這將是全球粉絲的線上投票。從現在到 4 月 20 日,Wonderous 粉絲和 Flutter 開發人員可以在 Webby 獎網站 上投票。


Wonderous 榮獲 Webby 獎提名 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

2023 年的 Flutter:策略與路線圖

作為一個開源專案,我們相信在我們對路線圖保持透明時,我們才能最好地服務我們的客戶。

對於採用 Flutter 等技術的開發人員來說,僅僅提供一組有用的功能還不夠。對 Flutter 的依賴也是對維護技能集和程式碼庫的長期承諾。因此,我們必須闡明一個令人信服且切合實際的願景和方向。我們希望分享更多關於我們(Google)為什麼投資 Flutter 的資訊,讓您對我們的未來和方向更有信心,並讓您能夠更清楚地規劃您的投資如何與我們的投資相連接或補充我們的投資。

**因此,我們今天分享了 2023 年策略文件**,其中我們表達了我們的宗旨和指導原則,並描述了我們計劃在今年剩餘時間裡進行的主要投資。出於必要性,我們進行了一些輕微的修改(例如商業敏感資料或對未發布產品的參考),並且像所有計劃一樣,我們不期望這個計劃能夠在面對現實時倖免於難。此策略文件應與我們 wiki 上的 工程路線圖 一起閱讀,該路線圖提供了我們正在開發的特定功能的更多細節。

最後,在以上句子中使用「我們」一詞很重要,這可能會被理解為「那些 Google 支付薪水讓他們在 Flutter 上工作的人」。我們希望 Flutter 貢獻者遠遠多於僅僅是 Google 的員工,但我們不聲稱代表他人的動機或他們可能獨立進行的工作。我們對這種合作表示衷心的感謝。


2023 年的 Flutter:策略與路線圖 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

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

案例研究:使用 Dart 平台原生指令建立一個豐富的跨平台函式庫,支援 Dart 和 Flutter 應用程式

我們很興奮地看到 MongoDB 今天宣布 Realm for Dart 和 Flutter 的正式發佈!Realm 是一個反應式、面向物件的跨平台資料庫,在應用程式開發人員中很受歡迎。換句話說,它非常適合 Dart 和 Flutter。因此,在過去的一年中,我們與 MongoDB 合作將 Realm 帶到 Dart 和 Flutter。查看 MongoDB 發布的 部落格文章,了解 Realm 提供的功能、自早期預覽版以來的改進以及未來計畫。我們認為 Realm 將非常適合 Dart 和 Flutter 開發人員。

使用 Dart 將書籍寫入 Realm 資料庫

在這篇伴隨文章中,我們將透過幕後一窺 Realm for Dart 和 Flutter 的建立過程,作為一個實際的範例,說明如何建立新的基於 Dart 的開發人員 SDK。

Realm 範例

在深入探討 Realm SDK 的實作之前,讓我們先從快速了解如何使用 SDK 作為在 Dart 終端應用程式 上工作的開發人員開始(注意:Flutter 應用程式的步驟略有不同;請參閱 文件 以獲取詳細資訊)。

首先,您定義資料模型。這可以使用帶有額外 Realm 注解的普通 Dart 類別來完成。以下是一個書籍的範例資料類別:

定義好模型,並完全生成資料模型(見下文)後,您可以開始將一些書籍寫入 Realm 資料庫:

Realm for Dart 和 Flutter 是如何建立的

既然我們已經涵蓋了基本知識,讓我們看看 MongoDB 團隊是如何使用許多 Dart 函式庫和工具建立 Realm for Dart 和 Flutter。我們將看看在初始化 Realm SDK 時會發生什麼,Realm SDK 如何建立在一個共用原生 C++ 函式庫之上,Realm 終端工具是如何建立的,以及 Realm 模型是如何生成的。

初始化 Realm SDK

Dart 和 Flutter 開發人員將首先將 Realm 加入到他們現有的應用程式中,然後運行安裝程式:

1
2
dart pub add realm_dart
dart run realm_dart install

第一步只是將 Realm SDK 加入到 pubspec.yaml 中,以 註冊它作為應用程式的相依。第二步運行 Realm 終端工具,要求它安裝 SDK。如果我們查看 安裝指令的原始碼,我們可以看到安裝會確定我們正在開發的作業系統,然後下載該作業系統的 Realm SDK 二進位檔案。

但是為什麼安裝指令要下載二進位檔案?Realm SDK 是一個非常全面的產品,可以在各種作業系統和開發人員框架上使用。為了避免必須建立大量 Realm SDK 的實例,Realm 團隊決定將核心 Realm SDK 實作為一個共用的 realm-core C++ 函式庫。因此,安裝指令真正做的事情是下載這個核心函式庫的編譯版本,適用於應用程式支援的所有平台。

調用 realm-core SDK

使用 Realm SDK 撰寫 Dart 或 Flutter 應用程式的使用者可以獲得不錯的強類型 Dart API,如本文開頭所示。這些 API 如何與 realm-core C++ 函式庫相對應?

如果我們瀏覽 Realm SDK 的 lib 目錄,我們會找到 realm_bindings.dart,它以以下內容開頭:

謎團解開了:Realm SDK 使用 Dart 對原生 API 的互操作性支援(透過 dart:ffi 函式庫)來調用 realm-core。為了避免必須為大量可用的 Realm API 手動撰寫 FFI 綁定,這些綁定是使用 Dart 的 ffigen 工具自動生成的。這是我們在 Dart 團隊中看到快速發展的模式。

Realm 終端工具

讓我們轉向 Realm 終端工具本身。在上面,我們運行了安裝指令,但我們也可以在沒有指定指令的情況下運行這個工具:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ dart run realm_dart
Realm commands for working with Realm Flutter & Dart SDKs.

Usage: dart run realm|realm_dart <command> [arguments]

Global options:
-h, --help Print this usage information.

Available commands:
generate Generate Realm objects from data model classes
install Download & install Realm native binaries into a Flutter or Dart project
metrics Report anonymized builder metrics to Realm

Run "dart run realm|realm_dart help <command>" for more information about a command.

您可能會注意到這個輸出看起來很像核心 dart 和 flutter 工具的輸出。這不是巧合;所有三個工具都是使用相同的 Dart 基礎函式庫建立的,這些函式庫來自 package:args,專門用於終端工具:

  • args 函式庫對將原始命令列參數解析為選項和標誌有豐富的支援。
  • command_runner 函式庫提供工具支援的指令結構(例如,安裝)。

生成 Realm 模型

生成指令特別有趣,因為它根據我們在「Realm 範例」章節中定義的帶有注解的 Dart 模型類別生成完整的 Realm 模型:

1
dart run realm_dart generate

生成指令是如何工作的?它依賴於 Realm 生成器,該生成器建立在 Dart 的 build_runner 之上,這是一個用於生成器的框架,它接收一組輸入檔案,然後建立新的輸出檔案。通常 - 如 Realm 案例 - 輸入是帶有注解的 Dart 原始碼檔案,輸出檔案是額外的生成的 Dart 原始碼。

結語

希望您喜歡這次對大型開發人員框架如何從一組可重複使用的 Dart 函式庫中建立起來的幕後一窺。如果您是框架開發人員,希望這能为您提供一些靈感,激發您下一个基於 Dart 的框架的構思。

對於 Dart 和 Flutter 應用程式開發人員來說,Realm 是 MongoDB 團隊提供的一個很棒的新產品。我們邀請您查看 Realm SDK 文件 以及今天的 Realm 部落格文章


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

undefined