0%

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

使用 Flutter 支援 iOS 14 和 Xcode 12

與往常一樣,我們的目標是讓開發人員在 Flutter 上取得成功,無論您要鎖定的平台為何。自 6 月宣布 iOS 14 以來,我們一直在努力為 Flutter 加入支援此版本的新功能,包括更新 Xcode 12 支援,以及加入支援 iOS 14 的新功能和改進。

如果您的 iOS 14 應用程式使用 TextFieldCupertinoTextFieldTextFormField,您需要確保它使用 Flutter 1.20 或更高版本構建,以確保您的使用者不會受到不必要的剪貼簿通知訊息的困擾,這符合 Apple 更新的剪貼簿政策。

否則,現有的生產應用程式將在您的終端使用者 iOS 14 設備上順利運行。 但是,如果您今天要將開發手機升級到 iOS 14 並希望繼續與 Flutter 一起使用它,您有兩個選擇。

您的第一個選擇是使用穩定頻道上的 Flutter 最新修補程式版本 (1.20.4),它支援將應用程式部署到實體 iOS 14 設備進行開發。不幸的是,還需要另一個更具侵入性(因此風險更大)的變更才能在 iOS 14 上進行除錯和熱重載。我們將此修復推遲到我們下一個穩定版本,我們計劃在下週釋出。

您的第二個選擇是使用我們今天釋出的即將到來的 Flutter 1.22 的 beta 版。此版本包含對 iOS 14 的更全面支援,包括更新的視覺效果、應用程式片段和 Xcode 12 支援。當它發佈到穩定頻道時,我們將分享更多關於其功能集的信息,但在此之前,我們建議您在需要立即支援 iOS 14 的情況下使用此 beta 版本。

若要取得 Flutter 1.22 beta 版,請使用以下命令:

1
2
$ flutter channel beta
$ flutter upgrade

無論哪種情況,您都需要查看 針對 iOS 14 開發頁面,以獲取有關使用 Flutter 鎖定 iOS 14 的有用資訊。如果您在 iOS 14 中遇到任何問題,請 在 Flutter 儲存庫中提交議題。如果您有任何問題,請在 flutter-dev 群組 中發布。


使用 Flutter 支援 iOS 14 和 Xcode 12 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

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

探索 Dart 中的集合

如果您曾經調用 add()addAll()map()toList() 來建立列表或映射,您可能需要查看 collection ifcollection forspreads。去年,Dart 在 2.3 版中加入了這些功能

在本文中,我們將研究集合,探索這些新功能,並查看一些有趣的範例。透過掌握這些功能,您可以使您的程式碼更簡潔、更易於閱讀。

集合

首先,我們需要了解什麼是集合。集合 是一個包含其他物件的物件。例如:

  • List:具有長度的有序物件集合(也稱為 陣列
  • Set:唯一物件的無序集合
  • Map:鍵值對的無序集合
  • Queue:可以在兩端添加/移除物件的有序集合
  • SplayTreeMap:基於自平衡二元樹的鍵值對的有序集合

這些類型在 dart:collection 套件中可用。如需更多集合類型,請在 pub.dev 上查看 package:collection

這些集合類型都實作了 Iterable,它提供了一些常用行為,例如在集合中的每個物件上運行函數、獲取第一個物件、確定集合的長度等等。

集合字面量

Dart 支援用於構造三種類型集合的語法:列表字面量 ([])、映射字面量 ({}) 和集合字面量(也是 {})。

以下是一個列表字面量:

1
2
3
4
5
6
7
List<String> getArtists() {
return [
'Picasso',
'Warhol',
'Monet',
];
}

以下是一個映射字面量:

1
2
3
4
5
6
7
Map<String, String> getArtistsByPainting {
return {
'The Old Guitarist': 'Picasso',
'Orange Prince': 'Warhol',
'The Water Lily Pond': 'Monet',
};
}

以下是一個集合字面量,在 Dart 2.3 中加入:

1
2
3
4
5
6
7
Set<String> getArtistsSet() {
return {
'Picasso',
'Warhol',
'Monet',
};
}

如果您想知道為什麼映射和集合可以使用相同的 {} 語法,那是因為 Dart 使用 類型推斷 來區分。類型系統根據參數 a 和 b 的類型確定類型。它通常可以根據內容確定這一點——例如,{1} 顯然是一個 Set,而 {1: 2} 顯然是一個 Map

注意: 使用 {} 預設構造一個映射。要建立一個集合,您可以使用泛型類型註釋:<String>{}。使用兩個泛型類型參數則建立一個映射:<String, String>{}

元素的類型

集合字面量中的每個項目通常是一個值或表達式,但也可以是以下新功能之一:collection ifcollection forspread。所有這些都被稱為 元素

每個元素解包零個或多個項目,並將它們放入周圍的集合中。例如,一個字串字面量(例如「oatmeal」)會產生一個項目,但 collection for 會解包 0 個或多個項目。這些功能也可以以有趣的方式組合,我們將在下面探討。

Spreads

spread 接收一個集合(例如,一個列表),並將其內容放入周圍的集合中:

1
2
3
4
5
6
List<String> combineLists(List<String> a, List<String> b) {
return [
...a,
...b,
];
}

前面的程式碼相當於:

1
2
3
4
5
6
List<String> combineLists(List<String> a, List<String> b) {
var list = [];
list.addAll(a);
list.addAll(b);
return list;
}

您也可以在映射和集合字面量中使用 spreads:

1
2
3
4
5
6
Map<String, String> combineMaps(Map<String, String> a, Map<String, String> b) {
return {
...a,
...b,
};
}
1
2
3
4
5
6
Set<String> combineSets(Set<String> a, Set<String> b) {
return {
...a,
...b,
};
}

在映射和集合中,當發生衝突時,b 的內容會覆蓋 a 中的內容。例如,調用 combineMaps({'foo': 'bar'}, {'foo': 'baz'}) 會產生一個包含 {'foo': 'baz'} 的映射。

Null-aware spreads (…?)

null-aware spread 僅當運算符後的表達式為非 null 時才將內容加入到集合中:

1
2
3
4
5
6
List<String> combineIfExists(List<String> a, List<String> b) {
return [
...?a,
...?b,
];
}
1
2
3
4
void main() {
var result = combineIfExists(['foo'], null);
print(result); // [foo]
}

Collection if

使用 ifelseelse if 關鍵字根據條件將某些內容加入到集合中。以下是一個使用 collection if 的範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Article {
String title;
DateTime date;

Article(this.title, this.date);

String toString() {
return [
if (title != null) title,
date.toString(),
].join(', ');
}
}

可以在末尾加入 else 關鍵字:

1
2
3
4
5
String toString() {
return [
if (title != null) title else '(no title)',
].join(', ');
}

請注意逗號的位置。逗號不能在 title 之後,因為 else 是同一個元素的一部分。將 ifelse 保持在一起,在逗號之前,可以將它們與集合中的下一個元素區分開來。

加入 else if 也可以:

1
2
3
4
5
6
7
8
9
10
11
String toString() {
return [
if (title != null) title else '(no title)',
if (date == null)
'(no date)'
else if (date.year == DateTime.now().year)
'this year'
else
'${date.year}',
].join(', ');
}

Collection for

最後,使用 for 關鍵字將序列插入到集合中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Article {
String title;
DateTime date;
List<String> tags;

Article(this.title, this.date, this.tags);

String toString() {
return [
title,
date.toString(),
for (var tag in tags) 'tag: $tag'
].join(', ');
}
}

在此範例中,for 表達式為 tags 列表中的每個項目加入一個字串。就像 Dart 中的普通 for 迴圈一樣,tags 表達式可以是任何 Iterable

Flutter 程式碼中的集合

如果您正在使用 Dart,很有可能您正在使用它來構建 Flutter 應用程式。由於這裡描述的功能是在設計時考慮到 Flutter 的,讓我們來看看一些 Flutter 程式碼。

重構 build() 方法

在 Flutter 中,通常在 build() 方法中構建 Widget 列表:

1
2
3
4
5
6
7
8
9
10
@override
Widget build(BuildContext context) {
var articleWidgets = articles
.map<Widget>((article) => ArticleWidget(article: article))
.toList();

return ListView(
children: articleWidgets,
);
}

可以使用 spread 重寫此程式碼:

1
2
3
4
5
6
7
8
Widget build(BuildContext context) {

return ListView(
children: [
...articles.map((article) => ArticleWidget(article:article))
],
);
}

或者使用 collection for

1
2
3
4
5
6
7
8
Widget build(BuildContext context) {
return ListView(
children: [
for (var article in articles)
ArticleWidget(article: article)
],
);
}

第一個程式碼片段使用 map()Article 類別轉換為 ArticleWidget 物件的集合,然後應用 spread 運算符將它們展開到周圍的列表中。在第二個範例中,collection for 運算符讓您可以更簡潔地表達這一點。

更大的 build() 方法

以下是一個更複雜的範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Widget build(BuildContext context) {
var headerStyle = Theme.of(context).textTheme.headline4;

return Column(
children: [
if (article.title != null)
Text(article.title, style: headerStyle),
if (article.date != null)
Text(article.date.toString()),
Text('Tags:'),
for (var tag in article.tags)
Text(tag),
],
);
}

放置 Widget 到 Column 中的邏輯就在讀者可能期望的位置,並且節省了大量程式碼。在這些功能出現之前,實現相同行為最常用的方法是建立一個變數,並使用調用 add() 的普通 if 陳述式。

組合這些功能

這些功能可以以有趣的方式組合,如本節中的範例所示。以下是一些需要注意的事項:

  • 從語法上講,collection ifcollection forspread 是一個 單個元素——即使它最終建立了多個物件。
  • 任何表達式都可以放在 collection ifcollection for 的主體中。
  • 任何 元素 都可以放在 collection ifcollection for 的主體中。

結合使用 if 和 for

以下是一些使用 collection for 內部的 collection if 建立列表的程式碼。在這裡,如果每個文章的日期晚於特定日期,則將其加入到列表中:

1
2
3
4
5
6
7
8
List<Article> recentArticles(List<Article> allArticles) {
var ninetyDaysAgo = DateTime.now().subtract(Duration(days: 90));
return [
for (var article in allArticles)
if (article.date.isAfter(ninetyDaysAgo))
article
];
}

如果您更喜歡 spreads,則返回值可以寫成 ...allArticles.where((article) => article.date.isAfter(ninetyDaysAgo))

將 collection if 和 spreads 結合使用

collection if 接收單個元素,但如果您想包含多個元素,則可以使用 spread

1
2
3
4
5
6
7
8
9
10
Widget build(BuildContext context) {
return Column(
children: [
if (article.date != null) ...[
Icon(Icons.calendar_today),
Text('${article.date}'),
],
],
);
}

將集合功能與 async-await 結合使用

您也可以將非同步調用與集合字面量結合使用。例如,一個常見的模式是使用 Future.wait() 觸發一組非同步調用:

1
2
3
4
5
6
7
8
9
Future<Article> fetchArticle(String id);

Future<List<Article>> fetchArticles() async {
return Future.wait([
fetchArticle('1'),
fetchArticle('2'),
fetchArticle('3'),
]);
}

可以使用 collection for 改進該程式碼:

1
2
3
4
5
6
Future<List<Article>> fetchArticles(List<String> ids) async {
return Future.wait([
for (var id in ids)
fetchArticle(id),
]);
}

也可以在集合字面量中放入 await,儘管它會依次等待每個 Future

1
2
3
4
5
6
7
Future<List<Article>> fetchArticles(List<String> ids) async {
return [
// 一次獲取一個
for (var id in ids)
await fetchArticle(id),
];
}

前面的程式碼會依次等待,因為它相當於以下程式碼:

1
2
3
4
5
6
7
8
Future<List<Article>> fetchArticles() async {
return <Article>[
// 一次獲取一個
await fetchArticle('1'),
await fetchArticle('2'),
await fetchArticle('3'),
];
}

您也可以使用 await for 展開 Stream

1
2
3
4
5
6
7
8
9
10
11
Stream<String> get idStream => Stream.fromIterable(['1','2','3']);
Future<List<String>> gatherIds(Stream<String> ids) async {
return [
await for (var id in ids)
id
];
}

void main() async {
print(await gatherIds(idStream)); // [1, 2, 3]
}

這是 collection ifcollection forspreads 如何與語言的其他部分一起使用的另一個範例。如果您使用過 await for 陳述式,您可能會猜到其行為:它偵聽 Stream 中的新值,並將主體放入周圍的列表中。

進一步探索

希望這些技巧能幫助您編寫更乾淨的 Dart 程式碼。除了這裡提到的之外,還有更多使用這些功能的方法。如果您發現了一個好的技巧,請與社群分享在 Twitter 上提及 @dart_lang。如需更多詳細資訊,請查看使 Dart 成為更好的 UI 語言或 GitHub 上的初始語言提案


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

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

Google 暑期程式碼計畫 2020 成果

Google Summer of Code

Google 暑期程式碼計畫 (GSoC) 是一個全球性的計畫,旨在讓學生開發者參與開放原始碼軟體開發。Google 贊助學生在暑假期間與一個指導性的開放原始碼組織合作,進行為期 3 個月的程式設計專案。

二月,我們很興奮地宣布 Dart 團隊將成為 2020 年 GSoC 的指導組織。我們最終 指導了 5 個專案,這些專案是從來自世界各地學生的 170 多份申請中選出的。今天,我們很高興與大家分享我們的成果,這些成果將由參與專案的學生們來描述。

使用 package:ffigen 為 Dart-C 互通性產生 dart:ffi 繫結

Prerak Mann

package:ffigen 是一個 FFI 產生器,它透過從 C 標頭檔 (.h) 產生 Dart 繫結,大大減少了使用 dart:ffi 的工作量。

為了理解這個工具的需求,請考慮 LibClang(Clang 的 C 介面)。LibClang 的 API 大約有 8,000 行程式碼 (LOC)。如果您要手動為 LibClang 編寫 Dart 繫結,您將需要將近 4,500 LOC。使用 package:ffigen,您只需傳入一個小於 20 LOC 的 小型 YAML 設定檔,然後就可以產生繫結。

您可以控制要產生的 C 函式,並使用簡單的正規表示式來重新命名巨集、列舉和個別的結構成員。您也可以 產生 巢狀在結構中的陣列成員(dart:ffi 中尚不完全支援)。

請查看 github.com/dart-lang/ffigen 以取得完整的 設定常見問題範例 等等。

使用新的 Flutter 範例學習測試

Abdullah Deshmukh

您已經使用 Flutter 一段時間了,但仍然不知道如何測試 Flutter 應用程式嗎?那麼您可能會對我們建立的範例感興趣,該範例展示了測試 Flutter 應用程式的最佳實踐。您將能夠快速輕鬆地學習在 Flutter 中進行測試。

範例 展示了不同類型的測試,包括:

  • 單元測試
  • Widget 測試
  • 整合測試
  • 效能測試
  • 狀態管理測試

作為額外福利,我們還建立了一個關於如何測試 Flutter 應用程式的程式碼實驗室。您可以在文章 使用新的 Flutter 範例學習測試 中查看專案下完成工作的詳細報告。

使用者可擴展的翻譯檔案格式

Jaime Blasco

intl_translation 套件僅支援 ARB 檔案格式,該格式在 Dart 之外並未廣泛使用。在 GSoC 2020 期間建立的 intl_translation_format 套件建基於 intl_translation,並添加了一個 TranslationFormat 類別,可以支援各種不同的格式。開發人員可以在自己的套件中添加對其他格式的支援,並且只需幾行程式碼即可建立與現有工具整合的提取和產生命令列工具。有了這些,其他格式(即使是專有格式)的現有翻譯可以輕鬆匯入到 Dart 專案中,並且源自 Dart 程式碼的新訊息可以整合到現有的翻譯工具和工作流程中。

作為這些功能的展示,新的套件包含對多種格式的支援。除了 ARB 之外,它還支援 XLIFF v1.2 (iOS)、XLIFF v2.0帶有 ICU 訊息的 JSON實驗性函式庫 展示了如何實作對其他格式的支援,甚至是可以在執行階段更新的翻譯。

行動應用程式和聯合 Plugin 中的平台通道

Ayush Bherwani

在研究了平台通道(用於在 Dart 與原生程式碼(如 Swift 或 Kotlin)之間來回傳送訊息的 Flutter API)之後,我們決定使用兩個不同的範例來介紹這個主題。一個是 簡單的 Android 和 iOS 範例,而另一個是 聯合 Plugin,能夠使用主頻道為 Web 和桌面建構。在這兩個範例之間,程式碼庫不僅展示了平台通道和用於移動資料的編解碼器的詳細資訊,還展示了聯合 Plugin 的 API 以及在 Web 和桌面(Windows 和 Linux)API 上調用原生程式碼的技術。

如果您是一位正在尋找建構 Plugin 或僅存取平台 API 的開發人員,這些範例是開始學習的好地方。有關此專案的更多資訊,請參閱文章 平台通道範例

編寫 package:yaml_edit 以啟用 pub add <package>

Garett Tok Ern Liang

Dart 專案在 pubspec.yaml 檔案中指定套件依賴項。YAML 很好,因為它允許我們編寫註釋,但是以程式設計方式修改 YAML 是一個挑戰。

為了應對這一挑戰,我們編寫了 package:yaml_edit 以輕鬆執行上下文感知的字串操作,從而保留格式和註釋。該套件的測試套件包含超過 40,000 個測試,我們認為我們有一個非常好的解決方案。試用它並 提交錯誤

使用 package:yaml_edit,我們在 pub(Dart 和 Flutter 的套件管理工具)中添加了一個新的子命令 pub add <package>pub add 會自動解析目標套件的最新相容版本,並將其添加到使用者的 pubspec.yaml 中。具體來說,pub add <package> 自動化了以下所有操作的繁瑣過程:

  1. 訪問 https://pub.dev/packages/
  2. 選擇要添加到套件的版本
  3. 修改 pubspec.yaml
  4. 執行 pub get
  5. 根據需要重複步驟 2-4 多次,以獲得與其他依賴項相容的版本

一個類似的移除依賴項的命令 - pub remove <package> - 也正在開發中。請密切關注即將在 pub 中推出的這兩個命令!

非常感謝所有向 Dart 申請 Google 暑期程式碼計畫 2020 的人,以及今年完成專案的學生,以及每週花時間讓這個夏天成為一個很棒的程式碼夏天的導師。我們希望在整個 Dart 生態系統中再次見到你們這些學生作為貢獻者。


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

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

隆重推出全新的 pub.dev

今天,我們推出了全新的 pub.dev 網站。它採用了新的評分指標,讓您可以更好地了解可能有用的套件,並為套件發布者提供更多可操作的回饋,以提高套件品質。為了幫助您找到支援您感興趣的所有平台的套件,我們新增了識別和搜尋多平台套件的功能。總體而言,我們重新設計了使用者介面,目標是提供與我們的 dart.devdartpad.dev 網站一致的愉悅而優雅的體驗。

A screenshot of the page at https://pub.dev
全新的 pub.dev 網站

找到最符合您需求的套件

在過去一年中發布了近 7,000 個新套件,在尋找擴展應用程式功能的套件時,您不乏選擇。但是有這麼多套件可供選擇,您自然希望最受歡迎、品質最高、最常用的套件能夠脫穎而出。為了支援更好的套件搜尋結果,我們圍繞三個維度重新設計了 pub.dev 中的評分系統:

Screenshot of a package summary: 552 likes; 100/110 pub points; 98% popularity
pub 套件的三個評分維度
  1. 點讚數: 衡量有多少開發者點讚了一個套件。這提供了一個對套件整體評價的原始指標。
  2. Pub 分數: 一種新的品質衡量標準。這包括品質的幾個維度,例如程式碼風格、平台支援和可維護性。詳情請見下文。
  3. 熱門度: 衡量有多少開發者使用一個套件。這反映了过去六十天內依賴該套件的應用程式數量。標準化後的評分範圍從 100%(最常用的套件)到 0%(最少使用的套件),但我們正在研究是否可以在未來版本中提供絕對使用計數。

我們希望這些新的維度可以幫助正在尋找依賴套件的套件使用者,以及致力於發布高品質套件的套件發布者。

使用 Pub 分數衡量品質

點讚數和熱門度提供了社群提供的、主觀的衡量標準,Pub 分數的目標是提供一個可衡量的、客觀品質的近似值。這並不容易;開發者以各種方式感知品質,但我們仍然相信大多數開發者都在尋找我們可以衡量的核心特徵。以前的 pub.dev 評分模型基於對問題的扣分。新的評分模型扭轉了這種方法,而是根據五個類別中的特徵授予分數:

Screenshot: Pub points overview for a package
Pub 分數概述,包含類別和檢查,以及獲得的分數
  1. 遵循 Dart 檔案規範: 遵循 pubspec、readme 和 changelog 檔案的最佳實務
  2. 提供文件: 提供 API 文件和說明性範例
  3. 支援多個平台: 支援盡可能多的 Dart 和 Flutter 平台
  4. 通過靜態分析: 提供無錯誤、警告和 lint 的程式碼
  5. 支援最新的相依: 具有支援最新 Dart 和 Flutter SDK 以及最新套件相依的相依限制

我們預計隨著時間的推移,會為 Pub 分數新增更多檢查和類別,提高套件可以獲得的分數上限。例如,我們計劃在該功能推出時,為支援 空安全 新增一個分數。我們正在研究如何衡量可維護性(問題和拉取請求的回應速度)、可測試性(自動化測試覆蓋率)和社群友好度(貢獻者、維護者數量等)。如果您對一個好的衡量標準有任何想法,我們很樂意 聽取您的意見

支援多平台應用程式

Dart 和 Flutter 都擅長建立針對多個平台的應用程式。在 pub.dev 上,我們為行動(iOS 和 Android)和 Web 平台提供了清晰的平台標籤,以及這些平台的搜尋篩選器:

Screenshot: Search results for Flutter packages that support both Android and Web
搜尋中的平台篩選器

為了確保您的套件標記了正確的平台支援,請遵循 pub.dev 評分說明頁面 上的指南。

當桌面平台(macOS、Windows 和 Linux)發展到更最終的狀態時,我們將新增類似的支援。目前,您可以在「進階」搜尋選項中找到這些平台的篩選器:

Screenshot: Search results for Flutter packages that support Android, iOS, and macOS platforms
進階篩選器中的桌面平台

優雅而令人愉悅的設計

Dart pub.dev 團隊與我們的內部創意團隊 Google Developer Studio 合作,Jeremy Sie 擔任主要設計師。除了功能更新之外,團隊還仔細研究了視覺設計,分享了他們對該專案的想法。為了提供一致的、易於存取的、以使用者為中心的體驗,我們根據 Google 的 Material Design 系統 進行了一系列改進。我們的目標是建立一個輕量級且簡化的介面,為套件發布者和使用者提供清晰簡潔的工作流程。

更新後的設計採用了 Material 元件,例如卡片和晶片,可以清晰地顯示套件詳細資訊,並讓單個套件的功能更加透明。我們更新了排版系統,並改進了整個網站的 字體比例,使搜尋結果更加清晰,並改進了套件資訊的顯示方式。套件分數等元件也經過重新設計,使其更加清晰,並以一種使評分過程更加透明且更容易理解的方式進行組織。

Screenshot: Package search results
搜尋顯示所有三個 pub.dev 分數

我們還對首頁進行了更改。我們知道快速搜尋套件的能力是網站的核心部分,但我們希望透過顯示最有用的工具和函式庫來改進體驗。我們在前面加入了「Flutter 精選」和「最熱門套件」等區塊,幫助您快速瀏覽一些最好的套件。我們仔細研究了調色板和藝術方向,加入了插圖,並使程式碼範例等元件與 dart.devdartpad.dev 在視覺上保持一致。

立即試用 pub.dev

我們邀請您立即試用新的 pub.dev 瀏覽和搜尋體驗,並使用新的評分維度和 Pub 分數報告。

如果您是套件開發者,我們鼓勵您查看套件的 Pub 分數,尋找改進套件的機會。有關評分系統如何運作的更多詳細資訊,請參閱 pub.dev 評分說明

與往常一樣,我們喜歡回饋。請在下方留言,或在 pub.dev 問題追蹤器 中提交問題。

作者:Michael Thomsen(Dart 產品經理)和 Jeremy Sie(Google Developer Studio 視覺設計師)


隆重推出全新的 pub.dev 最初發佈於 Dart 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

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

正確操作 Dart 字串 👉

像許多其他在表情符號開始主導我們的日常通訊和多語言支援在商業應用程式中興起之前設計的程式語言一樣,Dart 將字串表示為 UTF-16 編碼單元序列。這種編碼在大多數情況下都能正常工作,直到國際化的加強和伴隨任何語言出現的表情符號的引入,使得該編碼的固有問題成為每個人的問題。

考慮這個例子:

The image shows the string “Hello” with a handwaving emoji at the end and it’s UTF-16 code units. The emoji takes two units.

在字串 “Hello👋” 中,除了揮手表情符號 👋 之外,每個使用者可感知的字元都映射到一個編碼單元。這種映射的直接後果是對該字串長度的混淆。以下程式碼行的輸出是 6 還是 7?

1
print('Hello👋'.length);

對於使用者來說,這個字串中顯然有 6 個字元,除非你進行哲學思考。但 Dart String API 會告訴你 長度 是 7,或者更準確地說,是 7 個 UTF-16 編碼單元。這種差異會產生各種各樣的後果,因為許多文字操作任務都涉及到使用 String API 的字元索引。例如,”Hello👋”[5] 不會返回 👋 表情符號。相反,它會返回一個表示表情符號第一個編碼單元的錯誤字元。

好消息是,Dart 有一個名為 characters 的新套件,它操作使用者可感知的字元,而不是 UTF-16 編碼單元。但是,作為 Dart 程式設計師,您需要知道何時使用 characters 套件。我們的研究表明,即使是經驗豐富的 Dart 程式設計師在閱讀文字操作程式碼時也很容易忽略此類問題。在本文中,我將介紹一些常見的場景,在這些場景中您需要注意並考慮使用 characters 套件而不是 Dart String。

需要注意的場景

在本節中,我將介紹一些常見的文字操作場景,解釋為什麼在這些場景中使用 Dart 的 String API 可能會導致問題,並展示如何使用 characters 套件獲得更可靠的結果。以下用例通常假設我們正在處理人類使用者輸入的字串,其中可能包含表情符號或應用程式開發人員未預期的語言的字元。

場景 1:計算字串中的字元數

假設您正在撰寫一個函數,用於檢查使用者輸入的文字是否超過了特定數量的字元。如果未達到限制,則該函數返回剩餘字元的正數;如果超過了限制,則返回額外字元的負數。

使用 String API 執行此操作非常簡單:

1
2
3
int remainingCharacters(String text, int limit) {
return limit - text.length;
}

但是,以下測試揭示了此程式碼的問題:

1
2
3
test('remainingCharacters emoji', () {
expect(remainingCharacters('How are you doing today 😀', 50), 47);
});

以下是測試結果:

1
2
Expected: <47>
Actual: <46>

我們可以使用 characters 套件重寫此函數,該套件在 String 上提供了一個方便的擴展方法,以產生正確的字元數,如下所示:

1
2
3
int remainingCharacters(String text, int limit) {
return limit - text.characters.length;
}

場景 2:擷取子字串

在此場景中,我們想要實作一個函數,該函數從字串中刪除最後一個字元並將結果作為新字串返回。讓我們假設此字串來自使用者輸入。

使用 String 上的 substring 方法可以輕鬆實作此函數,如下所示:

1
2
3
String deleteLastCharacter(String text) {
return text.substring(0, text.length - 1);
}

但是,一個好的表情符號測試可以快速破壞程式碼:

1
2
3
test('deleteLastCharacter emoji', () {
expect(deleteLastCharacter('Hi 🇩🇪'), 'Hi ');
});

以下是測試結果:

1
2
3
Expected: ‘Hi ’
Actual: ‘Hi 🇩???’
Which: is different. Both strings start the same, but the actual value also has the following trailing characters: 🇩???

characters 套件可以輕鬆處理這種情況,因為它提供了高階方法,例如 skipLast(int count)。我們可以將此程式碼片段重寫為以下程式碼:

1
2
3
4
String deleteLastCharacter(String text) {
return text.characters.skipLast(1).toString();
}

場景 3:根據表情符號分割字串

在第三個場景中,我們想要根據給定的表情符號分割字串。以下是一個使用 String 上的 split 方法執行此操作的函數:

1
2
3
List<String> splitString(String text, String emoji) {
return text.split(emoji);
}

它會起作用嗎?它可能在 99% 的情況下都能正常工作,但以下測試說明了一個例子,其中上面的程式碼產生了相當令人驚訝的結果。

1
2
3
test('splitString emoji', () {
expect(splitString('abc👨‍👩‍👧‍👦abc abc abc', '👨‍👩‍👧‍👦'), ['abc', 'abc', 'abc', 'abc']);
});

以下是測試結果:

1
2
3
Expected: ['abc👨‍👩‍👧‍👦', 'abc', 'abc', 'abc']
Actual: ['abc👨‍👩‍', '‍👦', 'abc', 'abc', 'abc']
Which: was 'abc👨‍👩‍' instead of 'abc👨‍👩‍👧‍👦' at location [0]

那麼,為什麼當字串被分割時,👨‍👩‍👧‍👦 變成了兩個表情符號 👨‍👩?這是因為 👨‍👩‍👧‍👦 實際上由四個不同的表情符號組成:👨👩👧👦。當字串在 👧 上被分割時,“abc👨‍👩‍👧‍👦” 被分成了兩部分:“abc👨‍👩” 和 “‍👦”。

您可以透過使用 Characters 類別上的 split 方法來避免此問題,如下列程式碼所示:

1
2
3
List<String> splitString(String text, String emoji) {
return text.characters.split(emoji.characters).map((e) => e.toString()).toList();
}

場景 4:根據索引存取特定字元

在文字操作中,通常根據字串中的索引(即位置)存取特定字元。例如,以下程式碼片段顯示了一個函數,該函數從使用者在兩個單獨的文字欄位中輸入的名和姓中返回首字母縮寫:

1
2
3
String getInitials(String firstName, String lastName) {
return firstName[0] + lastName[0];
}

但是,正如我們在本文開頭所演示的那樣,在基於 UTF-16 的字串中使用索引可能會有風險。讓我們用以下測試案例驗證上述程式碼的正確性:

1
2
3
test('getInitials accent', () {
expect(getInitials('Élise', 'Boisson'), 'ÉB');
});

以下是測試結果:

1
2
3
Expected: ‘ÉB’
Actual: ‘EB’
Which: is different.

為什麼測試失敗了?這是因為字母 “É” 可能是 “E” 和重音符號的組合。您可以使用 characters 套件輕鬆避免此問題:

1
2
3
String getInitials(String firstName, String lastName) {
return firstName.characters.first.toString() + lastName.characters.first.toString();
}

練習:省略文字溢位

現在,這是一個挑戰。在此場景中,應用程式需要顯示訊息列表,每行顯示一條訊息。要求您檢查實作一個函數的程式碼,該函數在訊息長度超過給定字元限制時將文字溢位顯示為省略號。

1
2
3
4
5
6
String omitTextOverflow(String text, int limit) {
if (text.length &lt;= limit) {
return text;
}
return text.substring(0, limit - 3) + '...';
}

您能否提出一個測試來揭示此程式碼片段的潛在問題?您將如何使用 characters 套件重寫它?答案在本文末尾。

緩解措施和可能的長期解決方案

期望 Dart 使用者對上述陷阱保持高度警惕是不合理的。例如,在我們進行的一項實驗中,53.7% 的 Dart 使用者無法檢測到第一個場景(計算字元數)中說明的問題,即使他們在幾分鐘前收到了兩頁關於 characters 套件以及該套件旨在解決的問題的資訊。因此,我們正在採取兩階段方法來幫助開發人員為他們的文字操作需求選擇最合適的 API。

短期內,我們將在 Flutter 框架和 Dart 分析器中引入一組緩解措施,以使 characters 套件更容易在 Dart UI 程式設計中發現和調用。這涉及幾個步驟:

  1. 在 TextField Widget 的內部實作中使用 characters 套件。有關更多詳細資訊,請參閱 此 PR此設計文件
  2. 透過 Flutter 框架公開 characters 套件的 API。完成此操作後,Flutter 使用者將更有可能透過擴展方法 String.characters 發現 API,該方法將在對 String 執行自動完成時顯示。此工作的狀態在此議題中追蹤:https://github.com/flutter/flutter/issues/55593
  3. 更新 Flutter 框架的 API 文件和範例程式碼,以建議在適用時使用 Characters 類別,例如在 TextField.onChanged 的回調中。此工作在 https://github.com/flutter/flutter/issues/55598 中追蹤,相關詳細資訊在 此文件 中。
  4. 讓 Dart 分析器在自動完成用於處理使用者輸入文字的回調模板時建議將 String 物件轉換為 Characters 物件。例如,在使用者在 onChanged 上自動完成後,IDE 可以填寫以下程式碼片段中的所有內容。此工作在 https://github.com/dart-lang/sdk/issues/41677 中追蹤。
1
2
3
4
5
6
7
TextField(
onChanged: (text) {
final characters = text.characters;
// …
},
);

這些緩解措施可以提供幫助,但它們僅限於在 Flutter 專案上下文中執行的字串操作。我們需要在它們可用後仔細衡量它們的有效性。Dart 語言層面更完整的解決方案可能需要遷移至少一些現有程式碼,儘管一些選項(例如,靜態擴展類型可能會使重大變更易於管理。需要更多技術調查才能完全了解權衡取捨。

如何提供幫助

請幫助我們提高對如何使用 characters 套件修復字串問題的認識:

  • 在您自己的程式碼中尋找使用 String.lengthString.substring 的實例。如果字串可能源自使用者輸入,請嘗試使用 characters 套件重寫程式碼。
  • 與 Dart 社群中的其他人分享這篇文章。
  • 嘗試更新 StackOverflow 上關於 Dart 文字操作的 現有答案。如果已接受的答案忽略了 String API 的此限制,請提醒人們注意風險。
  • 對上面列出的 GitHub 議題發表評論,讓我們知道您的想法和意見。

現在,祝您編碼愉快 😉!

致謝

感謝 Kathy Walrath、Lasse Nielsen 和 Michael Thomson 審閱本文。我還要感謝參與我們使用者研究的開發人員。他們的參與幫助 Dart 和 Flutter 團隊更好地了解了處理 Dart String API 此限制的挑戰。


PS:以下是練習的解決方案:

1
2
3
4
5
6
7
8
9
10
String omitTextOverflow(String text, int limit) {
if (text.characters.length &lt;= limit) {
return text;
}
return text.characters.take(limit - 3).toString() + '...';
}

test("omitTextOverflow doesn't break emoji", () {
expect(omitTextOverflow("How are you doing today 😀", 20), "How are you doing...");
});


正確操作 Dart 字串 👉 最初發佈在 Dart 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

Flutter 釋出了網頁支援,允許您建立動態網站。Flutter 支援處理任何可能來自網頁使用的錯誤,例如 404 錯誤。但是,如何為您的 Flutter 應用程式製作自訂 404 頁面呢?

本文將說明如何建立在使用者導航到不存在的頁面時顯示的自訂頁面。

如何在 Flutter 中將客戶端重新導向到自訂 404 頁面

在瀏覽網際網路時,每個人都遇到過「404:頁面未找到錯誤」。Flutter 透過自動將您重新導向到初始路徑來處理此問題。這通常是您應用程式的首頁。但是,如果您想要像 [AirBnb](https://36bvmt283fg61unuud3h7qua-wpengine.netdna-ssl.com/wp-content/uploads/2013/03/airbnb-404.gif)、[GitHub](https://mamchenkov.net/wordpress/wp-content/uploads/2013/11/github-404.png) 甚至 [Flutter 網站](https://flutter.dev/foo) 那樣擁有華麗的 404 頁面該怎麼辦?您可以使用 Flutter 很輕鬆地做到這一點。

若要建立自訂 404 頁面,您的應用程式需要使用 MaterialApp、CupertinoApp 或 WidgetsApp Widget。大多數應用程式使用這三個 Widget 之一;它是在建立 Flutter 應用程式時呼叫的第一個 Widget。

MaterialApp 會將頂層 Navigator 組態設定為按以下順序搜尋路徑:

  1. 對於 `/` 路徑,如果非空,則使用 [home](https://api.flutter.dev/flutter/material/MaterialApp/home.html) 屬性。
  2. 否則,如果它包含該路徑的條目,則使用 [routes](https://api.flutter.dev/flutter/material/MaterialApp/routes.html) 表格。
  3. 否則,如果提供,則呼叫 [onGenerateRoute](https://api.flutter.dev/flutter/material/MaterialApp/onGenerateRoute.html)。它應該為任何有效的路徑返回一個非空值,這些路徑未由 [home](https://api.flutter.dev/flutter/material/MaterialApp/home.html) 和 [routes](https://api.flutter.dev/flutter/material/MaterialApp/routes.html) 處理。
  4. 最後,如果所有其他方法都失敗了,則呼叫 [onUnknownRoute](https://api.flutter.dev/flutter/material/MaterialApp/onUnknownRoute.html)。

如果您的路徑在這些表格中都沒有處理,則它使用 [onUnknownRoute](https://api.flutter.dev/flutter/material/MaterialApp/onUnknownRoute.html) 屬性來處理您的導航。此回呼通常用於錯誤處理。例如,此函數可能會始終產生一個「未找到」頁面,描述未找到的路徑。位置不明的路徑可能是由於應用程式中的錯誤或從 Android 意圖等外部請求推送路徑造成的。

以下範例程式碼演示如何為 onUnknownRoute 屬性定義一個匿名函數,該函數使用 RouteFactory,RouteFactory 是一個工廠方法,它以 RouteSettings 函數作為輸入,並返回一個 Route。以下程式碼片段顯示定義 onUnknownRoute 屬性可以多麼簡單:

1
2
3
onUnknownRoute: (settings) {
return MaterialPageRoute(builder: (_) => PageNotFound());
},

PageNotFound 是一個自訂 Widget,用於建立 404 頁面。此頁面可能會說明發生了什麼情況,並將使用者重新導向到首頁,但在建立 404 頁面時,您可以隨心所欲。

結束語

在建立 Flutter 應用程式時,重要的是要處理任何可能發生的問題。在 MaterialApp、CupertinoApp 或 WidgetApp 中使用 onUnknownRoute 屬性,可以讓您處理網站中不可避免的「頁面未找到」錯誤。

若要進一步了解 Flutter 中的路徑,請參閱 Medium 文章 [「使用命名路徑導航 URL 」](https://medium.com/flutter/flutter-web-navigating-urls-using-named-routes-307e1b1e2050)。

關於作者:Jose 最近從大學畢業,現在在 [Material](https://material.io) 工作,Material 是一個設計系統,幫助團隊建立高品質的數位體驗。Jose 的團隊維護 Flutter 的 [material 函式庫](https://api.flutter.dev/flutter/material/material-library.html)。若要進一步了解,請訪問 Jose 在 [GitHub](https://github.com/JoseAlba)、[LinkedIn](https://www.linkedin.com/in/josealba1996/)、[YouTube](https://www.youtube.com/channel/UCOdKA_On0oPe1tz02z1QfxA?view_as=subscriber) 和 [Instagram](https://www.instagram.com/jose.alba/) 上的頁面。


處理 Flutter 中的 404:頁面未找到錯誤 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

宣告可靠的空值安全

作者:Filip Hracek 和 Michael Thomsen

今天,Dart 團隊迎來了一個重要的里程碑,我們推出了空值安全功能的技術預覽版。空值安全可以幫助您避免一類通常難以發現的錯誤,並且額外帶來一系列效能提升。我們現在發佈的是早期技術預覽版,期待您的回饋。

這篇文章描述了 Dart 團隊推出空值安全的計劃。它還解釋了可靠的空值安全是什麼意思,以及它與許多其他語言採用的方法有何不同。

為什麼需要空值安全?

Dart 是一種型別安全語言。這意味著,當您取得某個型別的變數時,編譯器可以保證它是該型別的。但型別安全本身並不能保證變數不是空值。

空值錯誤非常常見。在 GitHub 上搜尋會發現由 Dart 程式碼中的空值引起的數千個問題,以及更多數千個提交試圖修復這些問題。

試著看看您是否可以在以下範例程式碼中發現可空性問題:

1
2
3
4
5
void printLengths(List<File> files) {
for (var file in files) {
print(file.lengthSync());
}
}

如果以空值呼叫此函式,它肯定會失敗,但還有第二種情況需要考慮:

1
2
3
4
5
6
7
void main() {
// 錯誤情況 1:將空值傳遞給 files。
printLengths(null);

// 錯誤情況 2:傳遞檔案列表,其中包含一個空值項目。
printLengths([File('filename1'), File('filename2'), null]);
}

空值安全功能可以解決這個問題:

有了空值安全,您可以更有信心地推斷您的程式碼。不再有煩人的執行階段空值解參考錯誤。相反,您會在編碼時得到靜態錯誤。

可靠的空值安全

Dart 的空值安全是可靠的。這意味著 Dart 可以 100% 確定在上面的範例中,files 列表及其中的元素都不能為空。當 Dart 分析您的程式碼並確定變數不可為空時,該變數始終不可為空:如果您在偵錯器中檢查正在執行的程式碼,您會看到不可空性在執行階段得以保留。相比之下,其他一些實作是不可靠的,並且在許多情況下仍然需要執行執行階段空值檢查。Dart 與 Swift 共享可靠的空值安全,但其他程式語言並不多。

Dart 的空值安全的可靠性還有另一個令人欣喜的含義:它意味著您的程式可以更小、更快。因為 Dart 確實確定 files 永遠不會為空,所以 Dart 可以優化。例如,Dart ahead-of-time (AOT) 編譯器可以產生更小、更快的原生程式碼,因為當它知道變數不為空時,它不需要加入空值檢查。

我們已經看到一些非常有希望的初步結果。例如,在模擬典型 Flutter 架構渲染模式的微基準測試中,我們看到了 19% 的效能提升

設計原則

在開始詳細設計空值安全之前,Dart 團隊定義了以下三個核心原則:

  • 預設不可為空。 除非您明確告訴 Dart 變數可以為空,否則它會將其視為不可為空。我們選擇此作為預設值,因為我們發現非空值是 API 中最常見的選擇。
  • 可逐步採用。 現有的 Dart 程式碼有很多。必須可以逐步遷移到空值安全,一部分一部分地遷移。在同一個專案中應該可以同時存在空值安全和非空值安全的程式碼。我們還將提供工具來幫助您進行遷移。
  • 完全可靠。 如上所述,Dart 的空值安全是可靠的。一旦您將整個專案和您的依賴項遷移到空值安全,您就可以獲得可靠性的全部好處。

使用空值安全宣告變數

核心語法很簡單。以下是一些以不同方式宣告的不可為空變數。記住,不可為空是預設值,因此這些宣告看起來和今天一樣,但它們的含義發生了變化。

1
2
3
4
// 在空值安全的 Dart 中,這些變數都不能為空。
var i = 42;
final b = Foo();
String m = '';

Dart 將確保您永遠不會將空值賦值給上述任何變數。如果您在一千行程式碼之後嘗試執行 i = null,您將收到靜態分析錯誤和紅色波浪線,並且您的程式將拒絕編譯。

如果您希望變數可以為空,您可以使用 ?,如下所示:

1
2
3
4
// 這些都是可為空的變數。
int? j = 1; // 之後可以為空。
final Foo? c = getFoo(); // 函式可能傳回空值。
String? n; // 最初為空。之後的任何時候也可以為空。

上述變數的行為與今天所有變數的行為相同。

您也可以在其他地方使用 ? 語法:

1
2
3
4
5
6
7
8
9
10
11
12
// 在函式參數中。
void boogie(int? count) {
// count 可能為空。
}

// 在函式傳回值中。
Foo? getFoo() {
// 可以傳回空值而不是 Foo。
}

// 也適用於:泛型、型別定義、型別檢查等。
// 以及上述的任何組合。

但是,再次強調,我們的夢想是您幾乎永遠不需要使用 ?。您的絕大多數型別都將是不可為空的。

讓空值安全更容易使用

Dart 團隊正在努力讓空值安全盡可能容易使用。例如,看看這段使用 if 檢查空值的程式碼:

1
2
3
4
5
6
7
8
9
10
void honk(int? loudness) {
if (loudness == null) {
// 未指定 loudness,以最大 loudness 通知開發人員。
_playSound('error.wav', volume: 11);
return;
}

// loudness 不為空,我們將其限制在可接受的級別。
_playSound('honk.wav', volume: loudness.clamp(0, 11));
}

請注意,Dart 如何聰明地意識到,當我們通過該 if 陳述句時,loudness 變數不可能為空。因此,Dart 允許我們直接呼叫 clamp() 方法,而無需額外操作。這種便利性是由一種稱為流程分析的東西實現的:Dart 分析器會像執行程式碼一樣瀏覽您的程式碼,自動找出有關程式碼的額外資訊。

以下是另一個範例,它顯示了一個 Dart 可以確定變數不為空的情況,因為我們總是為其賦值一個非空值:

1
2
3
4
5
6
7
8
9
10
11
12
13
int sign(int x) {
// 結果不可為空。
int result;

if (x >= 0) {
result = 1;
} else {
result = -1;
}

// 到這裡,Dart 知道 result 不可能為空。
return result;
}

如果您移除上述任何賦值(例如,刪除 result = -1; 行),Dart 就無法保證 result 將不為空:您將收到靜態錯誤,並且您的程式碼將無法編譯。

流程分析僅在函式內部有效。如果您有一個全域變數或類別欄位,那麼 Dart 就無法保證何時會為其賦值什麼值。Dart 無法模擬整個應用程式的流程。因此,當您知道變數在第一次讀取之前將不為空,但您無法立即初始化它時,可以使用新的 late 關鍵字。

1
2
3
4
5
6
7
class Goo {
late Viscosity v;

Goo(Material m) {
v = m.computeViscosity();
}
}

請注意,v 不可為空,儘管它最初未初始化。Dart 相信您在為 v 賦值一個非空值之前不會嘗試讀取它,並且您的程式碼可以編譯而不會出錯。

空值安全向下相容

Dart 團隊已經努力了一年多的時間,才讓空值安全進入技術預覽階段。這是自我們推出 Dart 2 以來,對 Dart 語言最大的補充。然而,它並不是一個重大變更。現有程式碼可以呼叫使用空值安全的程式碼,反之亦然。即使空值安全可用後,它也將是一個可選功能,您可以在準備就緒時採用。您現有的程式碼將繼續運行而不會發生變化

我們最近將 Dart 核心程式庫遷移到完全使用空值安全。作為空值安全向下相容的一個範例,我們替換了現有的核心程式庫,而沒有在 Dart 和 Flutter 測試環境中運行的現有測試和測試應用程式中造成任何損壞。我們甚至將新的核心程式庫推送到我們在 Google 內部的許多客戶,直接推送到他們的生產程式碼庫中,也沒有出現任何問題。我們計劃在該功能推出時將我們所有的套件和應用程式遷移到使用空值安全,我們希望您也能這樣做。但您可以按照自己的時間表,逐個套件、逐個應用程式地進行遷移。

空值安全路線圖

我們計劃分三個階段逐步推出空值安全:

  1. 技術預覽。 這是今天發佈的,並且在 Dart 的 dev channel 中可用。有關詳細資訊,請參閱下面的「立即試用」部分。事情仍然可能會發生變化,因此目前請勿在生產程式碼中使用空值安全。不過,請測試一下並 給我們回饋
  2. Beta 版。 空值安全將在 Dart 的 beta channel 中可用,並且不再需要 實驗性標誌。該功能將非常接近預期的最終版本。如果您擁有 pub.dev 套件或外掛,這是您可以開始遷移的時候,但您不應該將其發佈為穩定版本。
  3. 穩定版。 每個人都將擁有空值安全,並且您將被鼓勵將您遷移的套件和外掛發佈為穩定版本。您還應該遷移您的生產應用程式。

如果一切順利,我們計劃在今年年底之前將空值安全作為一個穩定功能發佈。從現在到那時,我們將加入工具來幫助您使您的程式碼空值安全,包括:

  • 一個遷移工具,以支援您自動執行升級現有套件和應用程式的許多步驟
  • pub.dev 上的標籤,以便您可以判斷套件是否支援空值安全
  • pub outdated 命令的擴展,支援尋找支援空值安全的最新版本的依賴項

立即試用

今天試用空值安全的最快方法是透過 nullsafety.dartpad.dev,這是一個啟用了空值安全的 DartPad 版本。打開「使用程式碼片段學習」下拉選單,可以找到一系列學習練習,逐步介紹空值安全的新語法和基礎知識。

您也可以在小型命令行程式中試用空值安全。(我們尚未遷移 Flutter 等較大的框架。)首先下載 dev channel Dart SDK 的副本,然後取得 此範例 Dart CLI 應用程式 的副本(GitHub 儲存庫zip 壓縮檔案)。範例應用程式的 README 檔案 中包含使用空值安全實驗標誌運行應用程式的說明。範例中的其他檔案提供了在 VS Code 和 Android Studio 中啟用偵錯的啟動設定。

我們也有文件,並計劃製作更多:

我們很高興將空值安全引入 Dart。可靠的空值安全是 Dart 的一個獨特功能,可以幫助您編寫更少錯誤的程式碼並獲得更好的效能。我們希望您在技術預覽中試用該功能,並透過我們的問題追蹤器 給我們回饋。編碼愉快!


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

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

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

黑人的命也是命

我們這週不會發佈 #FlutterGoodNewsWednesday 文章。

Flutter 和 Dart 團隊在此想藉此機會與黑人社群站在一起,並告訴您:我們與您同在,我們看到了您,我們支持您。本週美國發生的事件再次令人痛苦地意識到,系統性的種族主義仍然很普遍,正義在我們的社會中分配不均。

我們知道這些問題不是用三言兩語就能解決的;我們需要在個人和企業層面積極參與。我們也要承認,我們是問題的一部分,我們中的許多人需要努力檢視自己的偏見和特權。

我們鼓勵我們的團隊在本週花時間學習和反思如何成為更好的盟友,如何在我們自己的社群中更好地促進多元化。我們中有些人正在閱讀像 新吉姆·克勞 這樣的書籍,觀看像 第 13 號修正案 這樣的紀錄片;我們還發現這些指南很有幫助:白人銀河系指南留住眼淚:白人女性指南

我們將在本週剩下的時間保持安靜,專注於放大 有色人種的聲音


黑人的命也是命 最初發表在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

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

兩個月的 #FlutterGoodNewsWednesday

當我們意識到今年春天將會很不一樣,沒有往常的活動和連線點時,我們決定尋找一種新的方式,將我們的各種公告與您分享。我們中的幾個人聚在一起,於是 #FlutterGoodNewsWednesday 就誕生了。在過去的兩個月裡,我們每個星期都會分享一些新的內容,希望讓您露出笑容。本週,我們想追蹤一下我們迄今為止所做的部分公告,並分享一些進一步的更新。

Flutter 開發人員的免費入門培訓

我們以宣布與 App Brewery 的安吉拉·余合作創建的 免費 10 小時以上入門課程,涵蓋 Flutter 開發的基礎知識。我們很高興地看到超過 75,000 人註冊了這個課程,並且已經有將近 5,000 人完成了(我十三歲的兒子就是其中之一!)對於正在學習這個課程的大家,希望您能發現 Discord 群組,Very Good Ventures 和一組志願者一直在那裡提供禮賓服務。我們也一直在向大家學習——特別是了解開始學習時的一些早期陷阱;我們將利用這些知識來改進文件和產品本身。

如果您快完成這個課程了,您可能會想知道「下一步是什麼?」我們有一些建議,其中涵蓋了中級主題:

CodePen Flutter 支援

我們還分享了關於 CodePen 採用 Flutter 的資訊。迄今為止,我們已經看到來自世界各地的設計師和開發人員創建了近 5,000 個 Flutter pens。有些人的作品讓我們大開眼界。以下是我們過去幾週看到的一些最喜歡的:

使用 Flutter 的 Web 支援展示原型和實驗的新 CodePen。

繼續在 CodePen 上探索:我們將為您舉辦一場挑戰!

Flutter 強勁勢頭

在我們第三篇 #FlutterGoodNewsWednesday 部落格文章中,我們分享了一些有關 Flutter 採用的統計數據,現在有 200 萬開發人員使用 Flutter。與去年此時相比,現在每天使用 Flutter 的開發人員比過去一個星期還要多。

紐約時報拼字蜂遊戲,使用 Flutter 實作。

當時我們分享了我們看到大約 50,000 個應用程式上傳到 Play Store。增長速度繼續加快,我們很高興地分享自那篇文章發佈以來,又上傳了 10,000 個 Flutter 應用程式。這種勢頭激勵著我們:感謝您的支援!

其中一個新應用程式的絕佳範例來自 紐約時報遊戲團隊,他們在 Google I/O ‘19 活動 上分享了他們使用 Flutter 的一些經驗。他們的 拼字蜂 遊戲現已作為他們 字謎遊戲應用程式 的一部分推出到 iOS 和 Android 商店:這個令人上癮的文字遊戲完全使用 Flutter 構建,挑戰您用蜂巢狀字母製作單詞。

自這篇部落格文章發佈以來,我們還發佈了第一季度開發人員調查結果,有超過 6,000 人參與。94.5% 的人表示他們對 Flutter 感到滿意或非常滿意,但我們也更多地了解到哪些方面存在困難。以前的一份調查報告指出動畫是一個挑戰,我們隨後針對該子系統添加了一系列 視訊更詳細的文檔,以更深入地介紹它。

Web 進度更新

在過去的幾個月裡,我們一直在努力的一個領域是 Flutter 對目標 Web 輸出的支援。正如我們在 Web 更新中所報告的那樣,我們特別關注改進效能和一致性。在文章中,我們宣布 Flutter 現在支援漸進式 Web 應用程式,並且在後續的幾週中,我們一直在透過 更複雜的服務工作者快取 大幅改進此功能,以提高首次載入效能。

在過去的一週中,我們還開始了一系列 文章,介紹 基於優化 Flutter Gallery 的經驗教訓,針對在 Web 上運行的 Flutter 應用程式最大限度地提高效能的技巧和竅門。請繼續關注這個部落格頻道,在接下來的幾週中,我們將發佈更多關於此主題的文章!

Flutter 1.17 和 Dart 2.8

也許迄今為止所有這些公告中最大的新聞是 Flutter 1.17 的發佈,包括 Dart 2.8。Flutter 1.17 在所有平台上都提供了效能和記憶體改進,但您會在 iOS 上看到最大的提升,因為 採用了 Apple 的 Metal 子系統 進行圖形渲染。對於 Android 來說,您也會看到改進,特別是新的可選 快速啟動除錯模式,它在不更改程式碼的情況下提供了 3 倍的速度提升。

除了在部落格文章中提到的新功能之外,還有一個功能讓一些人注意到了,那就是在發佈模式構建中更新的錯誤顯示。對於發佈模式構建,我們已 停用當您的其中一個 Widget 配置不正確時出現的「紅色死亡畫面」。許多人告訴我們,他們希望在罕見的情況下,當您的應用程式中存在 bug 時,能夠看到一個不太……突出的訊息,希望這個更改的預設行為是一個更好的替代方案。(對於那些想要其他東西的人,您可以使用 ErrorWidget.builder 屬性進一步自訂它。)

令人興奮的是,截至撰寫本文時,三分之二的人已經升級到 1.17。我們鼓勵大家儘快升級,以利用此版本中提供的所有功能!

在此期間,我們為 Flutter 1.17 發佈了一個熱修補版本,並計劃根據需要進一步更新以提高穩定性和品質。您可以查看 相關 Wiki 頁面,了解每個熱修補版本中包含的 bug 修復,以及如何確保您正在運行最新的穩定版本。

Adobe XD Plugin 預覽可用性

對於設計師來說,另一個重大消息是 Adobe XD 對 Flutter 支援的第一個公開預覽,由 Adobe 本身發佈。

本週,Adobe 發佈了對 Plugin 的更新,其中 修復了一些與不透明度相關的匯出錯誤,並積極地組合形狀以獲得更清晰的 Dart 程式碼。此外,Adobe 上週發佈了一個 XD 更新,其中 包含用於存取響應式佈局的新 API。一旦它得到更廣泛的推出,請在接下來的幾週內繼續關注 XD 到 Flutter Plugin 的進一步更新,這將提高生成的 Dart 程式碼的靈活性。

Plugin 和 Flutter 最愛

最近,上週我們 宣布我們計劃將許多 Flutter 團隊的 Plugin 升級到 1.0,此外還有一組新的 Flutter 最愛,其中包括 使用 Apple 進行登錄

但始終值得指出一些默默無聞的英雄。有超過 10,000 個套件,很難全部認出來,但本週我們將重點介紹一個非正式的十佳清單,其中包含一些很酷的套件,這些套件還不是 Flutter 最愛,但仍然值得關注:

  1. fl_chart 提供精美的商務圖表,包括折線圖和條形圖、散點圖和餅圖。
  2. flutter_blurhash 為可能需要時間下載的圖片提供快速佔位符。
  3. font_awesome_flutter 提供數千個圖示,以便輕鬆將它們包含在您的 Flutter 應用程式中。
  4. fab_circular_menu 提供一個迷人的彈出式選單。
  5. flutter_staggered_animations 為 listview、gridview 和 columns 提供精美的載入動畫。
  6. device_preview 將您的應用程式限制在一個自訂設備殼中,例如,讓您能夠從 macOS 預覽 iPhone 輸出。
  7. smooth_page_indicator 允許您在資訊卡的旋轉木馬中進行轉場。
  8. flame 提供一個小型 Flutter 遊戲引擎,帶有一組豐富的教學課程。
  9. flutter_launcher_icons 是一個受歡迎的 CLI 工具,它極大地簡化了更新 Flutter 應用程式啟動器圖示的任務。
  10. smooth_star_rating 提供一個緊湊的控制項,用於選擇星級評分。
pub.dev 上的 10,000 多個 Plugin,其中包含從商務圖表到 2D 遊戲引擎支援到 Flutter 的所有內容。

順便說一下,如果您正在編寫套件,請確保您沒有錯過我們關於 開發現代 Plugin 的技巧和竅門的文章。並且請填寫我們的 季度調查,其中有一個專注於 Plugin 開發的模組。

Flutter 日

最後,但並非最不重要的一點——我們最近 宣布了 Flutter 日,它將在幾週後的 6 月 25 日舉行。我們將舉辦三個技術會議、#AskFlutter,以及一組新的和更新的程式碼實驗室,讓您為在隨後的週末開始的由社群主導的 #Hack20 黑客馬拉松 活動做好準備。

更多好消息即將到來

我們還沒有完成:我們還有許多公告將作為 #FlutterGoodNewsWednesday 的一部分推出。所以請繼續關注,如果您有自己的好消息要與我們分享,請不要猶豫!

您隨時可以在 Twitter 上透過 @flutterdev 聯繫我們。我們很樂意收到您的來信!


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

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