0%

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

Dart 2.17:提升生產力和整合能力

今天在 Google I/O 上,我們宣布推出新的 Dart SDK,版本 2.17。此版本基於我們提升生產力和平台可攜性的核心主題而構建。它提供了新的語言特性:支援成員的列舉、改進了向父類別轉發參數的功能,以及更靈活的命名參數。我們改進了工具,推出了 package:lints 的一個新的主要版本——我們的工具支援根據我們的最佳實務檢查 Dart 程式碼——並廣泛更新了核心函式庫 API 文件,其中包含豐富的程式碼範例。為了改進平台整合,我們提供了在 Flutter 外掛中使用 dart:ffi(原生 C 互通性)的新範本、對 RISC-V 處理器的實驗性支援,以及對簽署 macOS 和 Windows 可執行檔的支援。

Dart 2.17 reease banner

新的語言特性以提高生產力

我們不斷改進 Dart 語言,以提高您的生產力——透過新增新特性和改進現有特性。Dart 2.17 新增了對列舉成員的主要新支援,改進了您在建構函式中使用命名參數的方式,並使將參數轉發給父類別的程式碼更加簡潔,減少了重複。

增強的列舉支援成員

列舉非常適合表示一組離散的狀態。例如,我們可以將水建模為列舉 Water { frozen, lukewarm, boiling }。但是,如果我們想要在列舉上新增一些方法——例如,將每個狀態轉換為溫度,並支援將列舉轉換為字串,該怎麼辦?我們或許可以使用擴充方法來新增 waterToTemp() 方法,但我們必須小心地使其與列舉保持同步。對於字串轉換,我們更希望覆寫 toString(),但這一直不受支援。

在 Dart 2.17 中,我們現在全面支援列舉成員。這意味著我們可以新增儲存狀態的欄位、設定該狀態的建構函式、具有功能的方法,甚至覆寫現有的成員。你們中的許多人一直在要求此功能;這是我們在語言追蹤器中 投票數第三高的議題

對於我們的 water 範例,我們可以新增一個儲存溫度的 int 欄位和一個接受 int 的預設建構函式:

1
2
3
4
5
enum Water {

final int tempInFahrenheit;
const Water(this.tempInFahrenheit);
}

為了確保在建立列舉時調用建構函式,我們需要為每個列舉值調用它:

1
2
3
4
5
6
enum Water {
frozen(32),
lukewarm(100),
boiling(212);

}

為了支援轉換為字串,我們只需覆寫從 Object 繼承的 toString()

1
2
@override
String toString() => "The $name water is $tempInFahrenheit F.";

這樣,您就有一個完整的列舉,可以輕鬆地實例化,並且可以在其上調用方法:

1
2
3
void main() {
print(Water.frozen); // 輸出 “The frozen water is 32 F.”
}

這兩種方法的完整範例如下圖所示;我們發現新的 Dart 2.17 版本更易於閱讀和維護。

父類別初始化器

當您有一個類別繼承階層時,一種常見的模式是將一些建構函式參數傳遞給父類別的建構函式。為此,子類別需要 1) 在其自身的建構函式中列出每個參數,以及 2) 使用這些參數調用父類別建構函式。這會導致程式碼樣板化:大量重複,使程式碼更難閱讀,維護起來也更麻煩。

一些 Dart 社群成員幫助實現了這一點。GitHub 使用者 @roy-sianez 大約半年前提交了一個關於此的 語言議題;建議使用類似於 GitHub 使用者 @apps-transround 先前建議 的內容:我們或許可以透過引入一個新的結構來表示參數是在父類別中指定的來解決這個問題。我們認為這是一個很棒的主意,所以這已經新增到 Dart 2.17 中。從下面的範例中可以看出,這與 Flutter widget 程式碼特別相關。事實上,當我們將新特性應用於 Flutter 架構時,我們發現程式碼總共減少了 近兩千行

任何地方的命名參數

最後,我們改進了當您調用方法時命名參數的工作方式。以前,這些參數必須出現在方法的參數列表的最後。如果您希望將位置參數放在最後以使程式碼更具可讀性,這會很煩人。例如,請參閱以下對 List<T>.generate 建構函式的調用——以前,growable 參數必須放在最後,這使得它很容易在包含產生器本身的大型位置參數下方被遺漏。現在您可以按照自己的喜好排序它們,讓您可以將小型命名參數放在最前面,將產生器放在最後。

欲了解更多關於這三個特性的實際應用範例,請參閱我們更新的 列舉父類別初始化器命名參數 的範例。

生產力工具

繼續生產力主題,我們在核心工具中進行了多項改進。

在 Dart 2.14 中,我們引入了 package:lints,它與 Dart 分析器一起工作,幫助您編寫 Dart 程式碼,以防止錯誤,並使用規範的風格,從而更有效地進行程式碼審查。從那時起,分析器中提供了許多新的 lint,我們仔細地對其進行了分類,並從中為所有 Dart 程式碼選擇了 十個新的 lint,並專門為 Flutter 程式碼選擇了 兩個新的 lint。這些 lint 包括確保您的導入包含在您的 pubspec 檔案中,防止在類型參數上誤用 null 檢查,以及確保子屬性具有一致的風格。您可以使用一個簡單的命令升級到新的 lint:

  • 對於 Dart 套件:
    dart pub upgrade --major-versions lints
  • 對於 Flutter 套件:
    flutter pub upgrade --major-versions flutter_lints

SecureSockets 通常用於啟用透過 TLS 和 SSL 保護的 TCP sockets。在 Dart 2.17 之前,在開發過程中除錯這些 sockets 非常棘手,因為沒有辦法檢查安全資料流量。我們現在新增了對指定 keyLog 檔案的支援。指定後,當與伺服器交換新的 TLS 金鑰時,NSS 金鑰日誌格式 的一行文字會附加到檔案中。這使得網路流量分析器工具(例如 Wireshark)能夠解密透過 socket 傳送的內容。有關詳細資訊,請參閱 SecureSocket.connect() 的 API 文件。

由 dart doc 工具產生的 API 文件是大多數 Dart 開發人員學習新 API 的關鍵資產。雖然我們的 核心函式庫 API 長期以來都有豐富的文字描述,但許多開發人員告訴我們,他們更喜歡透過閱讀使用 API 的範例程式碼來學習 API。在 Dart 2.17 中,我們徹底修改了所有主要的程式碼函式庫,在瀏覽量最高的 200 個頁面中新增了範例程式碼,因此它們現在都有全面的範例程式碼。例如,比較 Dart 2.16 中 dart:convert 的文件 與 Dart 2.17 中更新的 頁面;希望這能使文件更易於使用。

生產力的提高不僅僅來自於我們為平台新增新特性,還來自於我們清理堆疊並移除不再使用的特性。這有助於保持我們平台的精簡,這對於新開發人員尤其重要。為此,我們從 dart:io 函式庫中移除了 231 行棄用的程式碼——如果您仍在使用這些棄用的 API,您可以使用 dart fix 升級到它們的替代品。我們也繼續努力移除 棄用的 Dart 命令列工具,這次移除了 dartdoc 工具(改用 dart doc)和 pub 工具(改用 dart pub 或 flutter pub)。

擴大我們的平台整合和支援

第二個核心主題是平台整合和支援。Dart 是一種真正的跨平台語言。雖然我們已經支援 各種平台,但我們仍在不斷改進,以確保您可以與每個支援的平台深度整合,並支援新興平台。

Dart FFI——我們與 C/原生程式碼互通的核心機制——是一種將 Dart 程式碼與現有原生平台程式碼整合的常用方法。在 Flutter 上,這可能是構建使用主機平台原生 API(例如 Windows win32 API)的 外掛 的好方法。在 Dart 2.17 和 Flutter 3 中,我們在 flutter 工具中新增了範本,因此您現在可以輕鬆建立 FFI 外掛,這些外掛具有由 dart:ffi 調用到原生程式碼的 Dart API。有關詳細資訊,請參閱 flutter.dev 上更新的 開發套件和外掛 頁面。

為了能夠在具有特定於其 ABI(應用程式二進位介面)的類型的平台上使用 FFI,FFI 現在支援特定於 ABI 的類型。例如,您現在可以使用 Long(C 語言中的 long)來正確表示具有特定於 ABI 大小的長整數,根據 CPU 架構,它可能是 32 位或 64 位。有關支援類型的完整列表,請參閱 AbiSpecificInteger API 頁面中的「實作者」列表。

當使用 Dart FFI 與原生平台深度整合時,您有時需要調整由 Dart 和原生程式碼分配的記憶體或其他資源(埠、檔案等)的清理。從歷史上看,這一直非常棘手,因為 Dart 是一種垃圾回收語言,會自動處理清理工作。Dart 2.17 透過引入 Finalizer 的概念來解決這個問題,Finalizer 包括一個 Finalizable 標記介面,用於「標記」不應過早完成或丟棄的物件,以及一個 NativeFinalizer 類別,可以附加到 Dart 物件上,以便在物件即將被垃圾回收時提供一個回調執行。這些一起允許在原生程式碼和 Dart 程式碼中執行清理程式碼。有關詳細資訊,請參閱 NativeFinalizer 的 API 文件中的描述和範例,或 WeakReferencesFinalizer 的文件,以了解在常規 Dart 程式碼中的類似支援。

我們對將 Dart 編譯為原生程式碼的支援是使 Flutter 應用程式具有出色的啟動效能和快速渲染的核心推動因素。第二個用例是使用 dart compile 將 Dart 編譯為可執行檔的能力。這些可執行檔可以在任何機器上獨立執行,而無需安裝 Dart SDK。Dart 2.17 中的另一個新功能是支援 簽署可執行檔,這使得在 Windows 和 macOS 上進行部署成為可能,因為在這些平台上通常需要簽署。

我們也繼續透過保持在新興平台的最前沿來擴大我們支援的平台集。RISC-V 是一種新的創新處理器指令集。RISC-V International 是一個全球非營利組織,擁有 RISC-V 規範,使指令集免費且開源。這仍然是一個新興平台,但我們對其潛力感到興奮,因此我們的 2.17.0-266.1.beta Linux 版本(或我們 beta 頻道 的更高版本)包含對它的實驗性支援。我們很樂意聽到您的回饋,所以請 提交議題發布 關於您的體驗!

開始使用 Dart 2.17!

我們希望今天的 Dart 2.17 版本能讓您興奮,提高您的生產力,並為您的應用程式實現更多的平台整合。要開始使用,您可以直接下載 Dart 2.17 版本,或者將其作為今天 Flutter 3 SDK 版本的一部分嵌入。

我們也邀請您查看我們為 Google I/O 提供的 新內容


Dart 2.17:生產力和整合 最初發佈在 Dart 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

將 Flutter 遊戲開發提升到新的境界

Flutter’s Dash, Android Jetpack, Chrome Dino, and Firebase’s Sparky gathering around a pinball machine.

為了 Google I/O,我們與 Flutter 團隊合作,重新構想了一款經典彈珠台遊戲,該遊戲使用 Flutter 和 Firebase 構建。以下是如何在 Flame 遊戲引擎的幫助下將 [I/O 彈珠台](https://pinball.flutter.dev/) 在網頁上呈現。

遊戲開發基礎

對於構建由使用者互動驅動的遊戲(例如益智遊戲和文字遊戲)來說,Flutter 架構是一個很好的選擇。當談到使用遊戲迴圈的遊戲時,[Flame](https://docs.flame-engine.org/)(一個建立在 Flutter 之上的二維遊戲引擎)可以成為一個有用的工具。I/O 彈珠台使用 Flame 的開箱即用功能,例如動畫、物理、碰撞偵測等等,同時也利用 Flutter 架構的基礎設施。如果您可以使用 Flutter 構建應用程式,那麼您已經擁有了使用 Flame 構建遊戲所需的基礎。

Flame engine logo

遊戲迴圈

在傳統應用程式中,螢幕通常在使用者發生事件或互動之前都是視覺上的靜態。對於遊戲來說,則相反 - UI 是持續渲染的,遊戲的狀態也在不斷變化。Flame 提供了一個遊戲 Widget,它在內部管理遊戲迴圈,以便 UI 能夠以高效能的方式持續渲染。`Game` 類別包含遊戲元件和邏輯的實作,這些實作傳遞給 Widget 樹中的 `GameWidget`。在 I/O 彈珠台中,遊戲迴圈會對彈珠在遊戲場上的位置和狀態做出反應,如果彈珠與物體發生碰撞或掉出了遊戲,則會應用必要的特效。

@override
void update(double dt) {
super.update(dt);
  final direction = -parent.body.linearVelocity.normalized();
angle = math.atan2(direction.x, -direction.y);
size = (_textureSize / 45) *
parent.body.fixtures.first.shape.radius;
}

使用二維元件渲染三維空間

構建 I/O 彈珠台的挑戰之一是弄清楚如何僅使用二維元素來建立三維效果。元件的順序決定了它們在螢幕上的渲染方式。例如,當彈珠向上發射到斜坡時,彈珠的順序會上升,因此它看起來像是位於斜坡的頂部。

I/O Pinball playfield featuring Flutter’s Dash, Android Jetpack, Chrome’s Dino, and Firebase’s Sparky, and other Google-themed elements. Toward the bottom of the board there are two flippers with two bumpers above and to the bottom right is the ball ready to be launched.

彈珠、彈射器、兩個彈射器和 Chrome 恐龍都是具有動態主體的元素,這些元素會受到世界物理的影響。彈珠的大小也會根據它在遊戲場上的位置而變化。當彈珠移動到遊戲場的頂部時,它的尺寸會縮小,從使用者的角度來看,它看起來離使用者更遠。此外,彈珠上的重力會根據彈珠台的角度進行調整,因此彈珠在斜坡上會下降得更快。

/// 根據彈珠在遊戲場上的位置縮放彈珠的主體和 Sprite。
class BallScalingBehavior extends Component with ParentIsA<Ball> {
@override
void update(double dt) {
super.update(dt);
final boardHeight = BoardDimensions.bounds.height;
const maxShrinkValue = BoardDimensions.perspectiveShrinkFactor;
    final standardizedYPosition = parent.body.position.y +   (boardHeight / 2);
final scaleFactor = maxShrinkValue +
((standardizedYPosition / boardHeight) * (1 - maxShrinkValue));
parent.body.fixtures.first.shape.radius = (Ball.size.x / 2) * scaleFactor;
final ballSprite = parent.descendants().whereType<SpriteComponent>();
if (ballSprite.isNotEmpty) {
ballSprite.single.scale.setValues(
scaleFactor,
scaleFactor,
);
}
}
}

使用 Forge 2D 實現物理

I/O 彈珠台很大程度上依賴於由 Flame 團隊維護的 [forge2d](https://pub.dev/packages/forge2d) 套件。此套件將開源 [Box2D 物理引擎](https://box2d.org/) 移植到 Dart 中,以便可以輕鬆地與 Flutter 整合。我們使用 forge2d 來為遊戲的物理提供動力,例如遊戲場上物體(Fixture)之間的碰撞偵測。

forge2D 允許我們監聽 Fixture 之間的碰撞事件。然後,我們為 Fixture 添加 `ContactCallbacks`,以便在兩個元素之間發生接觸時收到通知。例如,當彈珠(具有具有 `CircleShape` 的 Fixture)與彈珠台(具有具有 `EllipseShape` 的 Fixture)發生接觸時,得分會增加。在這些回呼函式中,我們可以精確設定接觸的開始和結束位置,因此當兩個元素與另一個元素發生接觸時,就會發生碰撞。

@override
Body createBody() {
final shape = CircleShape()..radius = size.x / 2;
final bodyDef = BodyDef(
position: initialPosition,
type: BodyType.dynamic,
userData: this,
);
  return world.createBody(bodyDef)
..createFixtureFromShape(shape, 1);
}

Sprite 表格動畫

彈珠台遊戲場上有一些元素,例如 Android、Dash、Sparky 和 Chrome 恐龍,它們是動畫的。對於這些元素,我們使用了 Sprite 表格,這些表格包含在 Flame 引擎中,並帶有 `SpriteAnimationComponent`。對於每個元素,我們都有一個檔案,其中包含圖像的不同方向、檔案中的幀數以及幀之間的時間。使用這些資料,Flame 中的 `SpriteAnimationComponent` 會將所有圖像在迴圈中編譯在一起,使元素看起來像動畫。

Sprite sheet showing the Android in various orientations so that if played on a loop, it will appear to be spinning in a circle.
Sprite sheet 範例
final spriteSheet = gameRef.images.fromCache(
Assets.images.android.spaceship.animatronic.keyName,
);
const amountPerRow = 18;
const amountPerColumn = 4;
final textureSize = Vector2(
spriteSheet.width / amountPerRow,
spriteSheet.height / amountPerColumn,
);
size = textureSize / 10;
animation = SpriteAnimation.fromFrameData(
spriteSheet,
SpriteAnimationData.sequenced(
amount: amountPerRow * amountPerColumn,
amountPerRow: amountPerRow,
stepTime: 1 / 24,
textureSize: textureSize,
),
);

更加深入地了解 I/O 彈珠台程式碼庫

Firebase 實時結果排行榜

I/O 彈珠台排行榜實時顯示世界各地玩家的最高分。使用者還可以將他們的得分分享到 Twitter 和 Facebook。我們使用 Firebase [Cloud Firestore](https://firebase.google.com/docs/firestore) 來追蹤前十名得分,並將其擷取以顯示在排行榜上。當新的得分寫入排行榜時,[Cloud Function](https://firebase.google.com/docs/functions) 會將得分按降序排序,並移除任何不在前十名的得分。

Leaderboard for I/O pinball with 10 top scores displayed.
/// 擷取前 10 個 [LeaderboardEntryData]。
Future<List<LeaderboardEntryData>> fetchTop10Leaderboard() async {
try {
final querySnapshot = await _firebaseFirestore
.collection(_leaderboardCollectionName)
.orderBy(_scoreFieldName, descending: true)
.limit(_leaderboardLimit)
.get();
final documents = querySnapshot.docs;
return documents.toLeaderboard();
} on LeaderboardDeserializationException {
rethrow;
} on Exception catch (error, stackTrace) {
throw FetchTop10LeaderboardException(error, stackTrace);
}
}

為網頁構建

與傳統應用程式相比,構建響應式的遊戲可能會更容易。彈珠台遊戲場只需要縮放到設備的大小即可。對於 I/O 彈珠台,我們根據設備的大小以固定比例進行縮放。這確保了無論顯示大小如何,座標系始終保持一致,這對於確保元件在設備之間一致地顯示和互動非常重要。

I/O 彈珠台還可以適應行動或桌面瀏覽器。在行動瀏覽器中,使用者可以點擊發射按鈕開始遊戲,也可以點擊螢幕的左右兩側來控制對應的彈射器。在桌面瀏覽器中,使用者可以使用鍵盤來發射彈珠和控制彈射器。

程式碼庫架構

彈珠台程式碼庫遵循分層架構,每個功能都在其自己的資料夾中。此專案中,遊戲邏輯也與視覺元件分開。這可以確保我們能夠輕鬆地獨立更新視覺元素,而與遊戲邏輯無關,反之亦然。

彈珠台的主題會根據使用者在開始遊戲之前選擇的角色而有所不同。主題由 `CharacterThemeCubit` 類別控制。根據角色選擇,彈珠顏色、背景和其他元素會更新。

Displays the different I/O Pinball themes. The top left shows Sparky, carpet with prominent flame decorations and neon orange lighting. The top right shows Dash, a carpet with prominent egg decorations and neon blue lighting. The bottom left shows Android, carpet with prominent Android Jetpack decorations and neon green lighting. The bottom right shows Chrome Dino, carpet with prominent cactus decorations, and neon white lighting.
/// {@template character_theme}
/// 用於建立角色主題的基類。
///
/// 角色特定的遊戲元件應在此處指定一個 getter,以
/// 載入其對應的遊戲資產。
/// {@endtemplate}
abstract class CharacterTheme extends Equatable {
/// {@macro character_theme}
const CharacterTheme();
/// 角色名稱。
String get name;
/// 彈珠的資產。
AssetGenImage get ball;
/// 背景的資產。
AssetGenImage get background;
/// 圖示資產。
AssetGenImage get icon;
/// 排行榜的圖示資產。
AssetGenImage get leaderboardIcon;
/// 閒置角色動畫的資產。
AssetGenImage get animation;
@override
List<Object> get props => [
name,
ball,
background,
icon,
leaderboardIcon,
animation,
];
}

I/O 彈珠台的遊戲狀態由 [flame_bloc](https://pub.dev/packages/flame_bloc) 處理,flame_bloc 是一個將 bloc 與 Flame 元件連接起來的套件。例如,我們使用 flame_bloc 來追蹤剩餘的遊戲回合數、透過遊戲獲得的任何獎金以及目前的遊戲得分。此外,Widget 樹的頂部還有一個 Widget,其中包含載入頁面的邏輯,包括如何玩遊戲的說明。我們還遵循 [行為模式](https://en.wikipedia.org/wiki/Behavioral_pattern) 來封裝和隔離遊戲功能的某些元素,這些元素基於其元件。例如,彈珠台在被彈珠擊中時會發出聲音,因此我們實作了 `BumperNoiseBehavior` 類別來處理此問題。

class BumperNoiseBehavior extends ContactBehavior {
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
readProvider<PinballPlayer>().play(PinballAudio.bumper);
}
}

程式碼庫還包含全面的單元、Widget 和黃金測試。有時,由於單個元件可能具有多個責任,這使得它們難以隔離測試,因此測試遊戲會帶來一些挑戰。結果,我們最終定義了一些模式來更好地隔離和測試元件。我們還將改進整合到 [flame_test](https://pub.dev/packages/flame_test) 套件中。

元件沙箱

此專案很大程度上依賴於 Flame 元件,以將彈珠台體驗呈現出來。程式碼庫附帶了一個元件沙箱,它類似於 [UI 元件庫](https://gallery.flutter.dev/#/)。這是在開發遊戲時的一個有用的工具,因為它允許您隔離開發遊戲元件,並確保它們在整合到遊戲中之前,看起來和行為符合預期。

Chrome Dino is animated, moving left to right and opening its mouth to shoot out the pinball ball.

接下來要做什麼

看看您是否可以在 [I/O 彈珠台](https://pinball.flutter.dev/) 中獲得高分!程式碼在 [這個 GitHub 儲存庫](https://github.com/flutter/pinball) 中是開源的。請關注排行榜,並在社交媒體上分享您的得分!


I/O 彈珠台由 Flutter 和 Firebase 提供支援 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

一步修復多個分析問題

快速修復分析問題中,我們探討了在編輯時自動修復個別診斷的支持。有時即使這樣也太慢了,您真的希望一次修復多個診斷。幸運的是,也有支持這樣做的,我們將在本文中介紹。

修復多個診斷的方法有幾種,取決於您要修復多少診斷,我們將分別研究每一種方法。

修復同一個診斷的多個出現位置

您是否曾經透過將類別移動到不同的檔案來重構程式碼?如果是這樣,很可能某些 import 僅僅是因為移動的類別中的引用才需要的,這會導致您留下未使用的 import:

Visual Studio Code
IntelliJ IDEA

對於任何可以在同一個檔案中多次應用的修復,工具都提供了一個選項,可以在檔案中的任何位置應用它。

這些修復會顯示在與其他修復相同的位置,並且標籤通常與單個位置修復相似,但會添加一些短語,例如「在檔案中」。例如,以下是此修復在 IDE 中的顯示方式:

Visual Studio Code
IntelliJ IDEA

選擇「在檔案中」修復會完全按照您的預期執行 - 它會移除檔案中所有未使用的 import:

Visual Studio Code
IntelliJ IDEA

修復檔案中的所有診斷

這很棒,但它只修復一種診斷,並且需要您找到報告診斷的位置(在編輯器中或在問題視圖中)才能調用修復。看起來工具還可以做得更多。

在 Visual Studio Code 中,它可以。您可以將修復應用於單個檔案中的所有診斷。為了了解如何操作,我們將使用與上面相同的示例:

如果您按一下滑鼠右鍵打開上下文選單,您將看到以下內容:

如果您選擇**原始碼動作…**,它會打開第二個選單:

選擇全部修復會將修復應用於編輯器中的所有診斷,產生以下結果:

您也可以從命令面板調用全部修復命令,您可以透過從檢視選單中選擇**命令面板…**或使用鍵盤快捷鍵(任何平台上的 F1,Windows 和 Linux 上的 Control+Shift+P 或 macOS 上的 Command+Shift+P)來打開命令面板。您可以透過輸入命令的名稱來篩選列表。命令面板看起來像這樣:

修復目錄中的所有診斷

我們用於修復診斷的最完整的工具是命令列工具 dart fix。它可以修復給定目錄中的所有診斷,這意味著您可以使用它來修復套件或應用程式中的所有內容。

當然,有一些注意事項。它並不能修復每個診斷,因為它應用與 IDE 中可用的快速修復相同的快速修復,並且並非每個診斷都有快速修復。當有多個同樣有效的方法來修復問題時,它也不會應用快速修復診斷,因為它不知道您更喜歡應用哪個修復。這些注意事項也適用於上一節中描述的全部修復命令。

雖然不需要經常使用此工具(這也是您可能不知道它的原因之一),但在某些情況下它特別有用。

首先,考慮您想要啟用新的 lint,甚至是 lints 集的情況,例如核心建議flutter規則集之一。您的套件中很可能至少有一些違反 lint 規則的情況。如果存在與 lint 相關聯的修復,那麼一步應用該修復到所有位置會比單獨應用修復到每個違規位置更方便。

其次,考慮您從一個 Flutter 版本更新到另一個版本的情況。如果兩個版本之間有 API 變更,則很有可能存在修復以升級您的程式碼以符合最新版本。同樣,如果所有這些變更都在單個操作中應用,則會很方便。

這就是 dart fix 命令的用武之地。它可以在多個檔案中應用針對多個診斷的快速修復。

該命令有兩種模式。「空運行」模式會告訴您可以應用多少變更以及可以修復哪些診斷,但不會修改任何檔案。「應用」模式會應用變更。

您可以提供包含要修復的檔案的目錄的路徑,但如果您省略目錄,它會遞迴地修復目前目錄及其子目錄中的所有檔案。

假設您已啟用 prefer_single_quotes lint,並且您有一個包含以下內容的檔案:

1
2
3
4
5
6
7
8
import "package:logging/logging.dart";

void main() {
var logger = Logger("myApp");
logger.info("Entering myApp");
// Do stuff.
logger.info("Exiting myApp");
}

從包含此檔案的目錄中使用 --dry-run 選項運行 dart fix 命令會產生以下輸出:

1
2
3
4
5
6
7
$ dart fix --dry-run
Computing fixes in __bin__ (dry run)... 4.3s

3 proposed fixes in 1 file.

demo.dart
prefer_single_quotes • 3 fixes

該工具已找到三個違反 lint 的地方,並報告它能夠修復所有三個,但它不會修改檔案。

再次運行 dart fix 工具,但這次使用 --apply 選項,會產生幾乎相同的輸出:

1
2
3
4
5
6
7
8
9
10
$ dart fix --apply
Computing fixes in __bin__... 5.3s
Applying fixes... 0.0s

3 proposed fixes in 1 file.

demo.dart
prefer_single_quotes • 3 fixes

3 fixes made in 1 file.

但這次檔案會更新為包含以下內容:

1
2
3
4
5
6
7
8
import 'package:logging/logging.dart';

void main() {
var logger = Logger('myApp');
logger.info('Entering myApp');
// Do stuff.
logger.info('Exiting myApp');
}

由於空運行模式不會產生任何允許您查看所建議的實際變更的 diff 輸出,請考慮在使用 --apply 選項運行工具之前提交您的變更。這樣,更容易查看工具實際進行了哪些變更,並還原您不喜歡的任何變更。

如果您不想在運行工具之前提交,並且您正在使用 IntelliJ IDEA,則可以使用檔案->本地歷史記錄功能來比較應用修復前後的狀態。(如需更多資訊,請參閱將標籤添加到特定狀態的本地歷史記錄。)

dart fix 命令無法修復所有問題,但如果可以的話,它比手動應用修復到所有位置要容易得多。

總結

能夠在單個操作中應用多個修復不僅提高了快速修復的功能,還可能更容易進行更大規模的變更,例如啟用新的 lint 規則或升級到最新版本的 Flutter。

與往常一樣,如果您對如何改進這些工具有任何想法,請透過建立 dart-lang/sdk 問題告知我們。

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

Flutter 是否提升了開發人員的生產力?

Flutter 團隊持續進行每季一次的使用者調查,以收集您,也就是 Flutter 開發人員的意見。2022 年的第一份調查於 2 月 22 日至 3 月 3 日進行,並透過 Flutter IDE 外掛、docs.flutter.dev 網站和 Twitter 進行訪問,超過 8,000 人參與了調查。

Flutter 仍然受到大多數開發人員的喜愛(93% 對 Flutter 感到滿意),甚至創下了有史以來「非常滿意」的最高百分比(60%)!這一點值得注意,因為自上次季度調查(2021 年 11 月至 2022 年 2 月)以來,Flutter 開發人員的數量增加了 10% 以上。我們很高兴看到,即使社群不斷增長,Flutter 仍然能够保持高水準的滿意度。

隨著時間推移,對 Flutter 的滿意度。

在此次調查中,我們重點關注以下四個主題:

  • IDE 外掛:我們可以做些什麼來改進 Flutter 的 IDE 外掛支援?
  • Dart 可擴展性:當 Flutter 專案規模越來越大時,Dart 工具是否會變慢或無回應?
  • 遊戲開發:您是否對遊戲開發感興趣? (更多資訊請參閱即將發布的部落格文章。)
  • Flutter 對開發人員的價值:Flutter 是否實現了對開發人員做出的承諾?

我們不會視您的支持為理所當然,我們會密切关注顯示我們可以進一步改進的指標。我們意识到,改进的空间总是存在的,在本文中,我们将告诉您更多关于我们所学到的知识以及这些信息如何推动我们的路线图。

IDE 外掛

根據此次調查,63.5% 的開發人員對 Flutter 的 IDE 外掛「非常滿意」。那麼其他開發人員呢?為什麼他們沒有完全滿意?

如以下圖所示,沒有任何單一項目能突出顯示為沒有對 IDE 外掛完全滿意的原因。對 Android Studio/IntelliJ 和 VS Code 用戶來說,缺少功能(35%)是最主要的原因,但外掛速度慢(29%)、記憶體消耗(28%)、外掛有 bug(26%)都收到了不少票數。(當受訪者點擊 IDE 中的調查連結時,會記錄 IDE 類型)。

沒有對 Flutter 的 IDE 外掛完全滿意的原因。

「IntelliJ」標籤也包含 Android Studio 用戶。

當我們詢問缺少或支援不完善的功能時,重構和快速修復被選為需要更好地支援的兩個最主要功能。這多少令人驚訝,因為我們沒有預料到重構會排名最高。此外,我們也驚訝地發現 Android Studio/IntelliJ 用戶非常關心導航,儘管我們理解 Android Studio/IntelliJ 用戶比 VS Code 用戶更關心 Android 整合。開發人員對 UI 建立器也有很大興趣。

IDE 中缺少或支援不足的功能。

總之,我們現在對 Flutter 開發人員的需求有了更清晰的理解。但是,我們將透過各種研究繼續了解改進關注領域所需的內容。

Dart 工具

Dart 工具(例如 Dart 分析器、編譯器、DevTools 和 IDE 外掛)使使用這種語言變得更加容易。我們從一些開發人員那裡得知,當 Flutter 專案規模很大時,Dart 工具可能會變慢甚至無回應。我們希望在下次調查中評估這個問題的普遍程度和嚴重程度。

從此次調查中我們發現,大約四分之一的開發人員遇到过這種情況(24.5%),其中近一半人表示,這對開發工作造成了極其負面的影響(16.7%)或中等負面的影響(29.6%)。

開發人員在 Flutter 專案規模越來越大時,遇到 Dart 工具變慢或無回應。
Dart 工具速度慢或無回應對生產力的影響。

我們預計這些數字會更低,我們渴望在這方面有所改進,因此我們計劃與一些對后续对话表示兴趣的開發人員聯繫,以重現問題,並最終提高工具的效能。

Flutter 對開發人員的價值

Flutter 承諾提供一個框架,可以從單個程式碼庫構建美麗、快速、跨平台的應用程式。在本次調查中,我們探討了 Flutter 開發人員是否認為我們實現了這些價值。

我們向那些將現有應用程式轉換為 Flutter 的開發人員和從頭開始創建新 Flutter 應用程式的開發人員提問了不同的問題。允許選擇多個答案,59.2% 的開發人員從頭開始創建了新應用程式,26.5% 的開發人員要么將現有應用程式轉換為 Flutter,要么將 Flutter 組件添加到現有應用程式中。

開發人員開發和發布 Flutter 應用程式的經驗。

對於那些使用 Flutter 創建新應用程式的開發人員,我們詢問他們在何種程度上同意關於與其他 Google 服務的整合、開發生產力和目標平台的三个陳述。我們發現,超過 90% 的開發人員同意「與我使用過的其他技術相比,Flutter 缩短了構建和發布新應用程式的時間」(92.1%),並且同意「Flutter 使我的應用程式能夠面向更多平台」。(92.2%)

使用 Flutter 創建新應用程式的影響。

類似的問題也詢問了那些將 Flutter 應用於現有應用程式的開發人員。90.7% 的開發人員同意「與之前相比,Flutter 使我的應用程式能夠更快速地開發」,85.3% 的開發人員同意「與之前相比,Flutter 使我的應用程式能夠在更多平台上提供」。我們還發現,84.8% 的開發人員同意「Flutter 使我的應用程式比以前更美觀」。

將 Flutter 應用於現有應用程式的影響。

當然,我們也聽到了 Flutter 可以改进的地方,例如為所有支援的平台提供更多外掛、添加更多文件、提高感知效能、更好地與其他工具整合等等。一些評論還提到,某些問題領域在多年的調查之後依然沒有改變。雖然我們努力解决這些問題並改進產品,但我們感謝您的耐心!不過,我們很高兴了解到,我们的開發人员正以更高效的方式面向更多平台。

總結

特別感謝所有參與我們調查的人!Flutter 團隊成員每季度都会花時間分析和消化反馈,并为下一个季度及以后做出决策。我們會繼續收集您的意見,以了解我們可以做些什麼來讓您使用 Flutter 的体验更加愉快和高效。

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

請繼續參與並分享您的想法。我們下次調查再见!


Flutter 是否提升了開發人員的生產力? 最初發佈在 Medium 上的 Flutter,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

敲鑼打鼓… 宣布 Flutter 拼圖 Hack 大賽得獎者!

The Flutter puzzle hack logo

Flutter 拼圖 Hack 大賽 邀請參賽者發揮創意,重新詮釋經典的滑動拼圖。可以說,作品讓評審們驚豔不已!

超過 5,500 位來自 120 多個國家、各個經驗層次的開發人員參與了 Flutter 拼圖 Hack 大賽。在公布各個類別的獲獎者之前,我們想向所有參與挑戰的開發者致敬。Flutter 社群再次團結一致,打造出非凡的成果。我們聚在一起參加了關於 動畫Firebase 主機滑動拼圖基礎 的演講。然後,看到 Flutter 開發人員打造出令人驚嘆的滑動拼圖陣容,一路分享亮點為您最喜歡的拼圖投票,真是令人興奮。感謝大家!

成為如此熱情且充滿創意的社群的一份子,真是太棒了。誰能想到有這麼多不同的方法來建立一個簡單的滑動拼圖遊戲?🤷‍♂️

廢話不多說… 頒獎時間到!

各個類別的獲獎者

最佳執行

Planets,作者:Jyotirmoy Paul

Flutter 拼圖 Hack 大賽獲獎者

這個專案在許多方面給評審們留下了深刻印象,包括出色的動畫運用、程式碼品質和純粹的創意。除了程式碼本身,Jyotirmoy 還分享了一些過程中很棒的學習時刻 - 看看他的分享,如果您有興趣。

最具創意

The Werewolves Escape,作者:Ahmad Ayaz Noor 和他的團隊

Flutter 拼圖 Hack 大賽獲獎者

這個功能豐富的專案結合了 Flutter 在網頁、行動和桌面端的優點。故事線讓滑動拼圖遊戲的趣味提升到了一個全新的層級。查看他們的 作品提交頁面,了解這個團隊在建立這個專案時學到了什麼。

最佳動畫/設計

Retro Slide Puzzle,作者:Ashish Beck

Flutter 拼圖 Hack 大賽獲獎者

這個拼圖的精緻視覺效果立刻吸引了評審們的注意。這個專案融合了流暢的 Rive 動畫、聲音外掛、自動完成和其他功能,讓遊戲體驗令人難以置信地令人滿足。

最佳跨平台

Slide Party,作者:Duong Bui Dai

Flutter 拼圖 Hack 大賽獲獎者

您是否曾經想過與朋友比賽,看誰能最快解開一個滑動拼圖?這個拼圖讓您夢想成真!不僅如此,由於 Flutter 的靈活性,玩家甚至不需要在同一台設備或作業系統上。

超過 40 位獲獎者!

儘管這些是各個類別的獲獎者,但從這麼多創意中選出獲獎者幾乎是不可能的。Flutter 拼圖 Hack 大賽還有超過 30 位其他獲獎者,以及許多其他優秀的參賽作品。前往 flutter.dev/events/puzzle-hack 查看獲獎者,並親自嘗試他們的拼圖!


宣布 Flutter 拼圖 Hack 大賽得獎者! 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

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

Dart 非同步程式設計:串流

一個顯示來自串流資料的簡單 Flutter 應用程式

本文涵蓋了反應式程式設計的基礎之一:串流,它們是 Stream 類型的物件。

如果您已閱讀我們先前關於 Futures 的文章,您可能記得 每個 Future 代表一個單一值(錯誤或資料),以非同步方式傳遞。串流的工作方式類似,只是它不是單一物件,串流可以隨著時間傳遞零個或多個值和錯誤

本文最初發表於 2020 年 2 月。此版本將包含的程式碼更新為可空安全性。

本文是基於《Flutter in Focus》影片系列《Dart 中的非同步程式設計》的第三篇文章。第一篇文章 Isolates and event loops 涵蓋了 Dart 支援背景工作的基礎知識。第二篇 Futures 討論了 Future 類別。

如果您喜歡透過觀看或聆聽來學習,本文中的所有內容都包含在以下影片中。

注意:本文中的程式碼已更新,以反映影片於 2019 年 6 月 28 日發佈後發生的最佳實務和 Dart 語言的變化(包括可空安全性)。

如果您考慮單一值與相同類型的 迭代器 的關係,那就是 Future 與串流的關係:Future 代表具有單一響應的單一請求,而串流代表具有多個響應的單一請求。

就像 Futures 一樣,關鍵是在事先決定 1) 當資料準備就緒時要做什麼,2) 發生錯誤時要做什麼,以及 3) 串流完成時要做什麼。與 Futures 一樣,在這個過程中,Dart 事件迴圈仍然在運行。

串流與 Dart 事件迴圈一起工作。

事件迴圈阻塞

例如,如果您使用 File 類別的 openRead() 方法從檔案讀取資料,此方法會返回一個串流。

資料塊從磁碟讀取並到達事件迴圈。Dart 函式庫查看它們並說:「啊,有人正在等待這個」,將資料加入到串流中,然後將其發送到您的應用程式。

當另一段資料到達時——它就會進入並出來。基於計時器的串流和從網路通訊端串流資料也與事件迴圈一起工作,使用時鐘和網路事件。

事件迴圈對資料進行排序。

監聽串流

接下來要了解的是如何使用串流提供的資料。

假設您有一個類別,它提供一個串流,每秒發出一個新的整數(1、2、3、4、5…)。您可以使用 listen() 方法訂閱串流。唯一的必需參數是一個函數。

1
2
3
4
5
final myStream = NumberCreator().stream;

final subscription = myStream.listen(
(data) => print('Data: $data'),
);

每次串流發出新值時,都會呼叫該函數並列印該值:

1
2
3
4
5
Data: 1
Data: 2
Data: 3
Data: 4
...

這就是 listen() 的工作原理。

重要:預設情況下,串流設定為單一訂閱。它們會保留其值,直到有人訂閱,並且它們在其整個生命週期中只允許一個監聽器。如果您嘗試兩次監聽同一個串流,則會收到異常。

幸運的是,Dart 也提供廣播串流。您可以使用 asBroadcastStream() 方法從單一訂閱串流建立廣播串流。廣播串流的工作方式與單一訂閱串流相同,但它們可以有多個監聽器。

廣播串流的另一個區別:如果在資料準備就緒時沒有人在監聽,則該資料會被丟棄。

1
2
3
4
5
6
7
8
9
final myStream = NumberCreator().stream;

final subscription = myStream.listen(
(data) => print('Data: $data'),
);

final subscription2 = myStream.listen(
(data) => print('Data again: $data'),
);

讓我們回到第一個 listen() 呼叫,因為還有一些事情要討論。

如前所述,串流可以像 Futures 一樣產生錯誤。透過將 onError 函數加入到 listen() 呼叫中,您可以捕獲和處理任何錯誤。

還有一個 cancelOnError 屬性,預設情況下為 true,但可以設定為 false,以便即使在發生錯誤後也能保持訂閱繼續進行。

您可以新增 onDone 函數,以便在串流完成傳送資料時執行一些程式碼,例如檔案已完全讀取完畢時。

結合所有四個參數——onErroronDonecancelOnError 和必需的參數 (onData)——您可以預先準備好應對任何情況。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
final myStream = NumberCreator().stream;

final subscription = myStream.listen(
(data) {
print('Data: $data');
},
onError: (err) {
print('Error!');
},
cancelOnError: false,
onDone: () {
print('Done!');
},
);

提示: listen() 返回的物件本身有一些有用的方法。它被稱為 StreamSubscription,您可以使用它來暫停、繼續,甚至取消資料流。

1
2
3
4
5
final subscription = myStream.listen(...);

subscription.pause();
subscription.resume();
subscription.cancel();

使用和操作串流

現在您已經知道如何使用 listen() 訂閱串流並接收資料事件,讓我們來談談讓串流真正酷炫的地方:操作它們。

一旦您在串流中獲得了資料,許多操作就會突然變得流暢而優雅。

讓我們回到之前的數字串流。

使用名為 map() 的方法,您可以從串流中獲取每個值,並動態地將其轉換為其他內容。給 map() 一個執行轉換的函數,它會返回一個新的串流,其類型與函數的返回值相符。

現在不再是整數串流,而是一個字串串流。在末尾呼叫一個 listen(),將 print() 函數傳遞給它,現在它會直接從串流中列印字串——非同步地,當它們到達時。

1
2
3
NumberCreator().stream
.map((i) => 'String $i')
.listen(print);
1
2
3
4
5
String 1
String 2
String 3
String 4
*/

您可以像這樣連結許多方法。例如,如果您只想列印偶數,則可以使用 where() 過濾串流。給它一個測試函數,該函數為每個元素返回一個布林值,它會返回一個新的串流,該串流只包含通過測試的值。

1
2
3
4
NumberCreator().stream
.where((i) => i % 2 == 0)
.map((i) => 'String $i')
.listen(print);
1
2
3
4
String 2
String 4
String 6
String 8

distinct() 方法是另一個好方法。對於使用 Redux store 的應用程式,該 store 會在 onChange 串流中發出新的應用程式狀態物件。

您可以使用 map() 將狀態物件串流轉換為應用程式一部分的視圖模型串流。然後,您可以使用 distinct() 方法獲取一個串流,該串流會過濾掉連續的相同值(以防 store 啟動一個不影響視圖模型中資料子集的更改)。

然後,您可以監聽並在獲得新的視圖模型時更新 UI。

1
2
3
4
myReduxStore.onChange
.map((s) => MyViewModel(s))
.distinct()
.listen( /* update UI */ )

Dart 內建了其他方法,您可以使用它們來調整和修改您的串流。此外,當您準備好使用更進階的功能時,還有 Dart 團隊維護的 async 套件,可在 pub.dev 上獲得。它包含可以合併兩個串流、快取結果以及執行其他基於串流的魔術的類別。

嘗試使用 async 套件來獲得更多基於串流的魔術。

若要獲得更多串流魔術,請查看 stream_transform 套件

建立串流

最後,還有一個值得一提的更進階的主題是如何建立您自己的串流。

就像 Futures 一樣,大多數情況下,您將使用網路函式庫、檔案函式庫、狀態管理等為您建立的串流,但您可以使用 StreamController 建立自己的串流。

讓我們回到我們一直使用的 NumberCreator 範例。這是它的實際程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
class NumberCreator {
NumberCreator() {
Timer.periodic(const Duration(seconds: 1), (timer) {
_controller.sink.add(_count);
_count += 1;
});
}

final _controller = StreamController<int>();
var _count = 0;
Stream<int> get stream => _controller.stream;
}

如您所見,它會持續計數,並使用計時器每秒增加一次計數。然而,有趣的是串流控制器。

StreamController 從頭開始建立一個全新的串流,並讓您可以存取它的兩端。有串流端本身,資料到達的地方。(我們在本文中一直在使用這個。)

1
Stream<int> get stream => _controller.stream;

還有接收端,這是將新資料加入到串流的地方:

1
_controller.sink.add(_count);

NumberCreator 使用了這兩個。當計時器關閉時,它會將最新的計數加入到控制器的接收端,然後它會使用公共屬性公開控制器的串流,以便其他物件可以訂閱它。

使用串流構建 Flutter Widget

現在我們已經介紹了建立、操作和監聽串流,讓我們來談談如何在 Flutter 中使用它們來構建 Widget。

如果您閱讀了先前關於 Futures 的文章,您可能記得 FutureBuilder。您給它一個 Future 和一個構建器方法,它會根據 Future 的狀態構建 Widget。

對於串流,有一個類似的 Widget 稱為 StreamBuilder。給它一個像 number creator 那樣的串流和一個構建器方法,它會在每次串流發出新值時重建其子 Widget。

1
2
3
4
5
6
7
StreamBuilder<String>(
stream: NumberCreator().stream.map((i) => 'String $i'),
builder: (context, snapshot) {
// 構建一些 Widget
throw UnimplementedError("Case not handled yet");
},
);

snapshot 參數是一個 AsyncSnapshot,就像 FutureBuilder 一樣。

1
2
3
4
5
6
7
8
9
StreamBuilder<String>(
stream: NumberCreator().stream.map((i) => 'String $i'),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text('No data yet.');
}
throw UnimplementedError("Case not handled yet");
},
);

您可以檢查其 connectionState 屬性以查看串流是否尚未傳送任何資料,或者它是否已完全完成。

1
2
3
4
5
6
7
8
9
10
11
StreamBuilder<String>(
stream: NumberCreator().stream.map((i) => 'String $i'),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text('No data yet.');
} else if (snapshot.connectionState == ConnectionState.done){
return const Text('Done!');
}
throw UnimplementedError("Case not handled yet");
},
);

您可以使用 hasError 屬性來處理資料值,並查看最新值是否為錯誤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
StreamBuilder<String>(
stream: NumberCreator().stream.map((i) => 'String $i'),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text('No data yet.');
} else if (snapshot.connectionState == ConnectionState.done) {
return const Text('Done!');
} else if (snapshot.hasError) {
return const Text('Error!');
} else {
return Text(snapshot.data ?? '');
}
},
);

最重要的是要確保您的構建器知道如何處理串流的所有可能狀態。一旦您掌握了這一點,它就可以對串流的任何操作做出反應。(有關更多資訊,包括您可以使用的 DartPad 實例,請參閱 StreamBuilder API 頁面。)

總結

本文涵蓋了串流的含義、如何從串流中獲取值、操作這些值的方法,以及 StreamBuilder 如何幫助您在 Flutter 應用程式中使用串流值。

您可以從 Dart 和 Flutter 文件中了解更多關於串流的資訊:

敬請期待本系列的更多文章。接下來我們將討論 asyncawait。這是 Dart 提供的兩個關鍵字,可幫助您保持非同步程式碼的簡潔性和可讀性。

同時,您可以在我們的 YouTube 頻道上觀看關於《Dart 中的非同步程式設計》的下一部影片系列,或者訪問我們的網站以獲取更多關於 DartFlutter 的資訊。


Dart 非同步程式設計:串流 最初發佈於 Medium 上的 Dart,人們在那裡透過醒目顯示和回應這個故事來繼續對話。

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

徵求 Google Summer of Code 2022 貢獻者

Google Summer of Code (GSoC) 是一個全球性的計畫,旨在讓學生開發者參與開源軟體開發。Google 贊助學生在夏季與一個指導性的開源組織合作進行一個 12 週以上的程式設計專案。在過去的 17 年裡,已有超過 18,000 名學生參與了 Google Summer of Code。

我們很高興地宣布,Dart 將在 2022 年的 Google Summer of Code 中擔任指導組織(這是第三次!)。查看成果文章,了解更多關於 2020 年和 2021 年的優秀專案。

您有興趣嗎?

現在就開始閱讀 專案構想列表,找到符合您的技能和興趣的專案。正式申請必須在 4 月 19 日之前提交,但鼓勵潛在申請者提交早期草稿並徵求回饋。

Dart 團隊預計會有足夠的導師,但只會接受少數申請,因此也請查看 其他指導組織

如果您有關於 Dart 和 GSoC 的具體問題,請在我們 專用的郵件列表 中提問。

有關 Google Summer of Code 的更多資訊,請觀看下面的影片。或者閱讀 Google Summer of Code 貢獻者指南

我們期待您的參與!


Google Summer of Code 2022 貢獻者 最初發佈於 Medium 的 Dart,人們在那裡透過醒目顯示和回應這個故事來繼續討論。

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

大型 Dart 專案的漸進式空安全遷移

對於一個簡單的小套件來說,Dart 空安全遷移 需要 1-2 個小時的工作量,但對於一個大型專案來說,這可能是一場長達數月的馬拉松。理想情況下,您希望逐步遷移您的專案——在馬拉松期間,您希望保持您的專案可擴展、可維護且易於發佈。

我已經將一個大型專案遷移到空安全,並決定整理一些步驟和技巧,以說明如何使遷移過程可靠且高效,希望它能節省您的時間。

步驟 1:轉換為非健全的空安全

首先,將您的專案遷移到非健全的空安全

首先將您的依賴項升級到空安全版本。非健全的空安全並不要求所有依賴項都是空安全的。但是,強烈建議等到所有上游依賴項都遷移完畢後再進行遷移,因為遷移依賴項可能會迫使您重新審視您自己程式碼中的遷移決策。對於相互依賴的套件,您可能被迫無序遷移,或者同時遷移這些套件(許多相互依賴的套件大多只在測試中相互引用)。請遵循 dart.dev 上的指南 在遷移程式碼之前盡可能升級更多依賴項。

接下來,更新套件的 Dart SDK,並使用以下步驟將每個未遷移的函式庫標記為 legacy:

  1. 確保您的 IDE(VSCode、IntelliJ / Android Studio)已安裝 Dart 外掛。
  2. 在 IDE 中打開您的套件,並確保沒有編譯錯誤。
  3. 更新 pubspec.yaml 檔案中的 dart_sdk 依賴項,以要求版本範圍:>=2.12.0 <3.0.0
  4. IDE 將突出顯示尚未啟用空安全的函式庫中的空安全相關錯誤。透過在每個受影響檔案的頂部加入註釋 // @dart=2.9 來移除錯誤。即使您的 main.dart 檔案沒有錯誤,也要將註釋加入其中,以保持應用程式以非健全模式運行,直到您準備好切換為止。
  5. 驗證所有測試是否通過,並將變更提交到您的主分支。如果您的測試已經是空安全的,您將需要命令列標誌 --no-sound-null-safety 來抑制空安全錯誤。

啟動應用程式時,請確保您在控制台中看到「以非健全的空安全模式運行」。

現在您已準備好一次遷移一個函式庫到健全的空安全。

步驟 2:迭代到健全的空安全

選擇一個或一組要遷移的函式庫。

專業提示:如果您選擇的函式庫很大,您可能希望在遷移之前將其分解成較小的函式庫。

使用 dart pub deps 建立專案的依賴圖。最好自下而上地遷移套件;從依賴樹中的葉子開始,然後向上迭代到根。但是,如果您的專案具有依賴循環,這可能是不可能的,而且不遵循此順序也沒關係。

使用 遷移工具 遷移函式庫(或一組函式庫):

  1. 透過執行 dart migrate --skip-import-check 啟動互動式遷移工具。您可能希望 cd 到包含所選函式庫的目錄,以便在樹中更容易導航。
  2. 透過取消選取左側面板中檔案視圖樹的根來取消選取所有內容。(如有興趣,請投票支持新增 取消選取所有 按鈕。)
  3. 使用 Control+F 找到您要遷移的檔案。
  4. 選取檔案,然後點擊 應用遷移。您可以透過兩種方式進行調整:(1) 在應用遷移之前使用註釋來調整工具的選擇,或 (2) 在應用遷移之後使用 IDE 來評估欄位、參數和變數的可空性。
  5. 在 IDE 中打開套件。修復錯誤,並在檔案中搜尋工具可能不準確的情況(請參見下面的潛在問題列表)。進行更正並使用 lint 警告以互動方式清理上游和下游程式碼。

您將無法修復兩個 lint 錯誤:

  1. import_of_legacy_library_into_null_safe(在已遷移的函式庫中)
  2. avoid_redundant_argument_values(在 legacy 函式庫中)

現在,使用註釋停用這些錯誤。您將在遷移完成後清理這些錯誤。

需要注意的潛在工具不準確性:

  1. 加入的類型 dynamicnum。您很可能知道應該使用哪種特定類型來代替。
  2. 在大多數情況下,bool? 可以透過預設值變為 bool
  3. 類型轉換(搜尋 as)可能意味著該工具沒有加入泛型類型參數。加入之後,lint 會指示轉換已變得不必要,可以移除。
  4. 在某些情況下,該工具會使泛型參數上的界限可空,而最好將其設為不可空(搜尋 ?>?,)。
  5. 該工具可能會使某些內容可空,而使用 latelate final 可以更好地表達,或者可以重構以允許在建構函式的初始化列表中進行初始化。(如有興趣,請投票支持新增 lint。)
  6. 使用空斷言運算子 ! 而不先檢查空值可能意味著變數或參數實際上應該是非空的。(如有興趣,請投票支持新增 lint。)
  7. 該工具以 collection as Iterable<TheType> 的形式為集合加入轉換。有時,此變更只是使已經隱式的轉換變得明確。但是,在其他情況下,由於泛型引數的可空性不匹配,這些轉換可能會引入 執行時錯誤。如有疑問,請考慮將轉換替換為每個元素的顯式轉換(例如,collection.cast<TheType>()),或考慮使用 package:collection 中的 whereNotNull 擴展方法
  8. 如果變數、欄位或參數是可空的,但可空性僅在測試中使用,則可能應該重構程式碼以移除該標識符的可空性。

(感謝 Kenzie Davisson 幫助我識別出這些情況。)

步驟 3:清理

遷移所有函式庫後,請執行一些最終清理:

  1. 清理禁用 lint 的註釋。
  2. 將剩餘的依賴項升級到空安全版本。
  3. 確保您的應用程式中沒有剩餘的 //@dart = 2.9 註釋。此時,啟動應用程式時,您應該在控制台中看到「以健全的空安全模式運行」。如果您沒有看到此訊息,則可能是您的函式庫尚未遷移(搜尋 // @dart = 2.9),或者依賴項尚未遷移。
  4. 確保應用程式仍然可以正常運行,並且測試通過。由於健全模式啟用了更強的執行時保證,因此當您啟用健全空安全時,您可能會看到新的執行時錯誤,您需要修復這些錯誤(儘管不太可能)。通常這是將可空集合(例如 List<int?>)轉換為不可空集合類型(例如 List<int>)的結果。

祝您遷移順利!


大型 Dart 專案的漸進式空安全遷移 最初發佈在 Dart 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

託管私有的 Dart 套件庫

我們最近發布的 Dart 2.15 版本增加了對私有套件庫的支援。套件庫是一個託管 Dart 套件的伺服器,供 dart pub 客戶端使用。這篇文章討論了如何將私有套件庫與 pub.dev 一起使用,並利用 Dart 2.15 中引入的新的權杖驗證機制。

此功能已被 CloudsmithJFrog Artifactory 採用,它們提供自定義套件庫作為服務:

Cloudsmith 和 JFrog Artifactory 現在都提供私有 Dart 儲存庫。

為什麼要使用自定義套件庫?

預設套件庫 pub.dev 由 Dart 團隊運營。它促進了 Dart 套件的發布以供公開使用,並允許任何 Dart 開發人員只需將套件的名稱添加到他們的 pubspec 檔案中即可使用這些套件。這使得活躍的生態系統可以建立豐富的 Dart 套件集合。但是,在以下情況下,自定義套件庫可能很有用:

  1. 在組織內共享私有套件。這可能是一個希望在其成員之間共享內部專有套件,而不是與公眾共享的組織。
  2. 在企業環境中嚴格控制依賴項。一些公司希望明確地為批准使用的特定 套件版本 建立允許清單。這可能是為了執行程式碼審查策略或許可證合規性。
  3. 沒有公共網際網路存取的 sécurisé 環境。一些組織在一個監管環境中運作,在該環境中不允許連接到公共網際網路,進而也不允許連接到 pub.dev。這通常適用於政府機構/承包商和一些銀行服務提供商。

只需要共享一些專有套件的組織(情況 1)可能會使用 git 依賴項。這是一個強大的機制,因為 dart pub 客戶端命令會調用 git 客戶端來複製 git 依賴項——透過這種方式,您可以使用 SSH 金鑰驗證複製依賴項,並使用 GitHub/GitLab 團隊管理權限。由於 GitHub 支援使用 Yubikey 的 SSH,此設定在某些情況下效果很好。

但是,在版本解決方面,dart pub 中的 git 依賴項有一些限制。當使用 git 依賴項時,dart pub 只會複製 pubspec.yaml 中指定的標籤/分支/引用,因為沒有嘗試多個版本依賴項的機制。因此,使用私有自定義套件庫可能很有吸引力,因為自定義套件庫可以向 dart pub 客戶端提供版本清單,允許 版本解決器 選擇相容的版本並避免衝突。它還可以輕鬆地使用 dart pub outdateddart pub upgrade --major-versions 升級私有和公共依賴項。

Git 依賴項不支援尋求 更嚴格的依賴項控制(情況 2)或使用 沒有網際網路存取的 sécurisé 環境(情況 3)的組織,因為許多套件必須鏡像。在這些情況下,幾乎總是最好只有一個自定義套件庫鏡像 pub.dev 的允許清單子集。

使用自定義套件庫

從 Dart 2.15 開始,您可以使用 簡短形式的託管依賴項 語法依賴於來自自定義套件庫的套件:

1
2
3
4
5
6
dependencies:
foo:
hosted: https://dart-packages.example.com
version: ^1.4.0
environment:
sdk: >=2.15.0 <3.0.0

簡短形式的語法需要 SDK 約束 >=2.15.0,因為舊版本的 Dart SDK 不支援此語法。如果您不想擔心語法,也可以只使用 dart pub add 命令從自定義套件庫中添加依賴項:

1
2
3
4
$ dart pub add foo --hosted https://dart-packages.example.com
Resolving dependencies...
+ foo 1.4.0
Changed 1 dependency!

使用託管依賴項語法,可以將來自自定義套件庫的依賴項與來自官方套件庫的依賴項混合使用。以下範例依賴於來自 dart-packages.example.com 儲存庫的套件 foo 和來自 pub.dev 的套件 retry

1
2
3
4
5
6
7
dependencies:
retry: ^3.0.0
foo:
hosted: https://dart-packages.example.com
version: ^1.4.0
environment:
sdk: >=2.15.0 <3.0.0

當使用自定義套件庫共享私有套件時(情況 1),這很有用。但是對於 更嚴格的依賴項控制(情況 2)或在 沒有網際網路存取的 sécurisé 環境(情況 3)中工作,最好覆蓋預設套件庫。

覆蓋預設套件庫

預設情況下,dart pub get 會從 pub.dev 獲取依賴項,除非使用託管依賴項語法指定自定義套件庫。但是,可以使用環境變數 PUB_HOSTED_URL 覆蓋 預設套件庫。當鏡像 pub.dev 的子集時(情況 2 或 3),此方法特別有用,因為無需更新 pubspec.yaml 檔案以引用自定義套件庫 URL。例如,只需編寫以下內容即可:

1
2
3
dependencies:
retry: ^3.0.0
foo: ^1.0.0

如果套件 retryfoo 的允許清單版本被複製到自定義套件庫,並且環境變數 PUB_HOSTED_URL 指向自定義套件庫 URL,則 pub get 可以按如下方式工作:

1
2
3
4
5
6
$ export PUB_HOSTED_URL=https://dart-packages.example.com
$ dart pub get
Resolving dependencies...
+ retry 3.1.0
+ foo 1.4.0
Changed 2 dependencies!

當建置伺服器位於公司防火牆後面,並且不允許外部網路連線以避免當有人忘記設定 PUB_HOSTED_URL 時的意外注入攻擊時,這種方法效果最佳。同樣,建議在 pubspec.yaml 中設定 publish_to: <hosted-url>,以避免意外發布到 pub.dev(當未定義 PUB_HOSTED_URL 時)。

針對自定義套件庫進行驗證

大多數自定義套件庫可能是需要驗證的私有套件庫。Dart 2.15 引入了 dart pub token 命令來管理驗證權杖。對自定義套件庫的請求使用秘密權杖進行驗證。您可以從自定義套件庫獲取秘密權杖,並將其傳遞給 dart pub token add <hosted-url>,它會提示輸入權杖,如下所示:

1
2
3
$ dart pub token add https://dart-packages.example.com
Enter secret token: [輸入秘密權杖]
Requests to "dart-packages.example.com" will now be authenticated using the secret token.

從 CI 環境進行驗證

在 CI 中運行時,通常可以將 秘密儲存在環境變數中,雖然可以使用 echo $TOKEN | dart pub token add <hosted-url> 傳遞秘密,但也可以告訴 dart pub 在與特定自定義儲存庫通信時從環境變數中讀取秘密:

1
2
$ dart pub token add https://dart-packages.example.com --env-var MY_SECRET_TOKEN
Requests to "https://dart-packages.example.com" will now be authenticated using the secret token stored in the environment variable "MY_SECRET_TOKEN".

這可確保 dart pub 不會實際將秘密儲存在其設定檔中,而只是儲存它應該從環境變數 $MY_SECRET_TOKEN 中讀取秘密的事實。這允許在 CI 環境中操作時僅將秘密儲存在環境變數中,並降低了如果執行環境在 CI 作業之間共享時秘密意外洩露的風險。

發布到自定義套件庫

要將套件發布到自定義套件庫,您需要在 pubspec.yaml 中指定 publish_to: <hosted-url>,然後運行 dart pub publish。這使用與 dart pub get 使用的相同的權杖進行驗證。至少,您的 pubspec.yaml 檔案應如下所示:

1
2
3
4
5
6
7
name: mypkg
version: 1.0.0
publish_to: https://dart-packages.example.com
dependencies:
meta: ^1.7.0
environment:
sdk: >=2.15.0 <3.0.0

運行 dart pub publish 時,務必查看提供的資訊。在確認發布操作之前,您應始終執行以下操作:

  • 檢查套件將發布到的 URL。
  • 查看要包含在套件中的檔案清單。
  • 考慮套件驗證建議。例如:
1
2
3
4
5
6
7
8
9
10
11
$ dart pub publish
Publishing mypkg 1.0.0 to https://dart-packages.example.com
|-- CHANGELOG.md
|-- LICENSE
|-- README.md
|-- lib
| '-- mypkg.dart
'-- pubspec.yaml
Package validation found the following potential issue:
* It's strongly recommended to include a "homepage" or "repository" field in your pubspec.yaml
...

對於發布到自定義儲存庫,建議 可能不 重要,但提供套件的元資料通常很有用。在 pub.dev 上,具有正確元資料和文件的套件得分更高 pub 分數

可以透過使用環境變數 PUB_HOSTED_URL 覆蓋 預設套件庫 來發布到自定義儲存庫,但是,如果您這樣做,強烈建議您在 pubspec.yaml 檔案中指定 publish_to: <hosted-url>;這可以防止您意外地將專有套件發布到公共儲存庫。如果您不想將專有套件發布到 任何 套件儲存庫,請指定 publish_to: none 以防止意外發布。

取得自定義套件庫

如前所述,自定義套件庫可作為多個商業供應商提供的服務,從而減輕您託管和維護自己的自定義套件庫的負擔。

Cloudsmith 中的權杖驗證

自 2020 年以來,Cloudsmith 一直提供私有和公共 Dart 套件庫。Cloudsmith 最近宣布 支援其 Dart 套件庫產品的權杖驗證。有關更多資訊,請查看 Cloudsmith 文件

由 Cloudsmith 託管的私有 Dart 套件庫。

JFrog Artifactory 中的 Dart 支援

JFrog Artifactory 最近 宣布支援自定義 Dart 套件庫,包括支援分層儲存庫和從 pub.dev 上的公共套件庫鏡像套件。有關更多資訊,請查看 JFrog Artifactory 文件

由 JFrog 管理的自定義 Dart 套件庫。

結語

對於那些想要編寫自己的自定義套件庫的人,我們發布了一個關於 提供 Dart 套件庫的規範。它相當簡單,但如果您不清楚某些方面,或者您有任何改進建議,請隨時在 pub.dev 儲存庫 上提交問題。


託管私有 Dart 套件庫 最初發佈在 Medium 的 Dart 上,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

在提交截止日期之前完成你的專案!

距離 Flutter Puzzle Hack 提交截止日期只剩下幾天了。我們對到目前為止看到的拼圖感到興奮,並且迫不及待地想看到更多!別忘了,獎品包括 4 台配備齊全的 MacBook、大量 Flutter 周邊商品以及超過 40 位獲獎者的現金,因此請務必提交您的作品,以爭取獲得這些很棒的獎品。

歡迎來到 Flutter 社群

隨著最終提交日期的迅速臨近,我們想藉此機會歡迎許多新開發人員加入 Flutter 社群。我們迫不及待地想看看你們會做出什麼,當然,從你們的 Flutter Puzzle Hack 提交開始!

宣布我們的評審

為了慶祝 Flutter Puzzle Hack 的最後一周,我們很高興能宣布我們的評審,包括許多自 Flutter 成立以來就一直幫助構建此框架的 Flutter 老將。提交拼圖 Hack 作品是一個絕佳的機會,可以讓您在世界上頂尖的 Flutter 專家評審您的作品。

  • Justin McCandless,Flutter 框架軟體工程師
  • Casey Hillers,Flutter 工程軟體工程師
  • Christian Padilla,Flutter 外掛軟體工程師
  • Devon Carew,Flutter 軟體工程師
  • Greg Spencer,Flutter 框架/桌面軟體工程師
  • Michael Goderbauer,Flutter 框架首席軟體工程師
  • Craig Labenz,Flutter 開發者關係工程師
  • Yusuf Mohsinally,Flutter 工程軟體工程師
  • Laura Willis,Flutter 和 Firebase 產品行銷主管
  • Jay Chang,Flutter 產品行銷經理
  • Kelvin Boateng,Flutter 產品行銷經理
  • Huan Lin,Flutter on iOS 軟體工程師
  • Eric Windmill,Firebase 和 Flutter 開發者關係工程師

告訴我們您何時提交

無論您還在構思您的想法,還是已經提交了您的應用程式,請在 Twitter 上使用標籤 #FlutterPuzzleHack 標記我們,我們一定會與 Flutter 社群分享您的作品。

例如,看看 Twitter 使用者 Roaa 正在為 這個 作品做些什麼:

仍然有時間!

如果您還在考慮開始提交,現在還不晚。查看 樣板應用程式部落格文章 解釋了應用程式的工作原理,然後前往 flutterhack.devpost.com 查看挑戰規則。


距離 Flutter Puzzle Hack 提交截止日期只剩下幾天了 + 宣布評審 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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