編碼 · 程式語言
Dart 入門教學
簡答
Dart 是 Google 推出的靜態型別物件導向語言,以 Flutter 應用程式框架使用的語言最為人熟知。它具備健全的空值安全,可編譯成原生程式碼、JavaScript 或 WebAssembly,開發時還有快速的熱重載。本頁依序介紹環境設定、型別、空值安全、流程控制、函式、集合、類別、非同步程式碼與錯誤處理。
Dart 是什麼、可以拿來做什麼
Dart 是 Google 開發、針對用戶端最佳化的程式語言。它是具備型別推斷的靜態型別語言,擁有健全的空值安全,既能編譯成原生機器碼(用於行動裝置、桌面與伺服器),也能編譯成 JavaScript 或 WebAssembly(用於網頁)。開發期間它執行在支援熱重載的虛擬機器上,修改後幾秒內就能在執行中的應用程式看到結果。
多數人學 Dart 是為了用 Flutter 開發應用程式。Flutter 是 Google 的 UI 工具包,一份程式碼就能建置 Android、iOS、網頁、Windows、macOS 與 Linux 應用程式。Dart 也用於命令列工具與後端服務。
安裝 Dart 並執行 Hello, world
請從 dart.dev 安裝 Dart SDK(或安裝已內含 Dart 的 Flutter)。每個 Dart 程式都從頂層的 main 函式開始執行。
// hello.dart
void main() {
print('Hello, world!');
}dart run hello.dart
# Hello, world!
dart compile exe hello.dart # builds a native executable
dart create my_app # creates a new console project本頁的所有範例也都可以在 DartPad 中試跑,它是位於 dartpad.dev 的瀏覽器線上編輯器。
變數與基本型別
用 var 讓編譯器推斷型別,用 final 宣告只會指派一次的變數,用 const 宣告編譯期常數。你也可以隨時改成明確寫出型別。
void main() {
var name = 'Dart'; // inferred as String
int year = 2011; // explicit type
final now = DateTime.now(); // set once, at run time
const pi = 3.14159; // compile-time constant
double ratio = 2; // int literal becomes 2.0
bool ok = year > 2000;
print('$name since $year, pi=${pi.toStringAsFixed(2)}, ok=$ok, ratio=$ratio');
// Dart since 2011, pi=3.14, ok=true, ratio=2.0
print(now.year > 2000); // true
}| 型別 | 範例 | 說明 |
|---|---|---|
int | 42 | 整數;在原生平台上為 64 位元 |
double | 3.14 | 64 位元浮點數 |
num | 7 或 7.5 | int 與 double 的父型別 |
bool | true | 條件式中只允許 true 與 false |
String | 'hi' | UTF-16;支援 $x 與 ${expr} 插值 |
dynamic | 任何值 | 關閉靜態檢查;大多數情況下應避免使用 |
在網頁平台上,int 值會以 JavaScript 數字表示,因此非常大的整數在那裡可能會損失精度。
健全的空值安全
自 Dart 3 起,所有 Dart 程式碼都採用健全的空值安全。型別為 String 的變數永遠不會是 null;若要允許 null,請寫成 String?。編譯器會檢查每一處使用,因此 null 錯誤在程式執行前就能被發現。
int lengthOf(String? s) {
// return s.length; // compile error: s might be null
if (s == null) return 0;
return s.length; // promoted to String after the check
}
void main() {
String? nickname;
print(nickname?.length); // null (null-aware access)
print(nickname ?? 'guest'); // guest (default if null)
nickname ??= 'dee'; // assign only if null
print(lengthOf(nickname)); // 3
late String config; // initialized later, checked at run time
config = 'loaded';
print(config); // loaded
}流程控制
Dart 有常見的 if、for、while 與 do-while,另有可走訪任何可迭代物件的 for-in 迴圈。自 Dart 3 起,switch 支援模式比對,也可以當作運算式使用。
void main() {
var scores = [72, 95, 48];
for (var s in scores) {
if (s >= 90) {
print('$s excellent');
} else if (s >= 50) {
print('$s pass');
} else {
print('$s fail');
}
}
for (var i = 0; i < 3; i++) {
print('i = $i');
}
var day = 6;
var kind = switch (day) {
6 || 7 => 'weekend',
>= 1 && <= 5 => 'weekday',
_ => 'invalid',
};
print(kind); // weekend
}函式
函式是一等物件。參數可以是位置參數、放在 [] 內的選擇性位置參數,或放在 {} 內的具名參數。具名參數除非標記為 required,否則都是選擇性的。箭頭語法 => 是只回傳一個運算式的函式的簡寫。
int add(int a, int b) => a + b;
String greet(String name, {String greeting = 'Hello', required bool loud}) {
var text = '$greeting, $name';
return loud ? text.toUpperCase() : text;
}
String label(String text, [int? width]) =>
width == null ? text : text.padRight(width, '.');
void main() {
print(add(2, 3)); // 5
print(greet('Ana', loud: false)); // Hello, Ana
print(greet('Ana', greeting: 'Hi', loud: true)); // HI, ANA
print(label('id', 5)); // id...
var twice = (int x) => x * 2; // anonymous function
print([1, 2, 3].map(twice).toList()); // [2, 4, 6]
}集合:List、Set、Map 與 Record
核心集合有 List(有順序)、Set(元素不重複)與 Map(鍵值對)。集合字面值支援 if、for 與展開運算子 ...。Dart 3 新增的 Record 可以把多個值組合在一起,不必另外宣告類別。
void main() {
var nums = [3, 1, 2]; // List<int>
nums.add(4);
nums.sort();
print(nums); // [1, 2, 3, 4]
var tags = {'dart', 'flutter', 'dart'};
print(tags.length); // 2
var ages = {'Ann': 31, 'Bo': 25}; // Map<String, int>
ages['Cy'] = 40;
print(ages['Bo']); // 25
var showAll = false;
var menu = ['Home', if (showAll) 'Admin', for (var n in nums) 'Item $n'];
print(menu); // [Home, Item 1, Item 2, Item 3, Item 4]
(String, int) pair = ('Ann', 31); // a record
var (who, age) = pair; // destructuring
print('$who is $age'); // Ann is 31
}類別與 async/await
Dart 是物件導向語言:每個值都是物件。類別支援帶有初始化形式參數(this.x)的建構函式、具名建構函式、getter、mixin 與介面。非同步程式碼使用 Future 與 Stream,搭配 async 和 await,讓 Flutter 應用程式在等待檔案或網路時仍保持順暢回應。
class Point {
final double x, y;
const Point(this.x, this.y);
Point.origin() : x = 0, y = 0; // named constructor
double get sum => x + y; // getter
@override
String toString() => 'Point($x, $y)';
}
Future<String> fetchUser() async {
await Future.delayed(Duration(milliseconds: 100));
return 'ana';
}
Future<void> main() async {
print(Point(1, 2).sum); // 3.0
print(Point.origin()); // Point(0.0, 0.0)
var user = await fetchUser();
print('user: $user'); // user: ana
}錯誤處理
用 throw 拋出例外,用 try、on、catch 與 finally 處理。使用 on SomeType 可捕捉特定型別。Dart 區分 Exception(程式可以處理的預期問題)與 Error(程式錯誤,例如斷言失敗或範圍錯誤),後者通常不應該捕捉。
int parseAge(String s) {
var n = int.tryParse(s); // null instead of throwing
if (n == null || n < 0) {
throw FormatException('Not a valid age: $s');
}
return n;
}
void main() {
for (var input in ['42', 'abc']) {
try {
print(parseAge(input));
} on FormatException catch (e) {
print('Error: ${e.message}');
} finally {
print('checked $input');
}
}
}
// 42 / checked 42 / Error: Not a valid age: abc / checked abc完整的小程式:購物車總額
這個程式用類別表示商品、用 Map 儲存購物車,並印出收據。它結合了類別、Map、字串格式化與集合方法。
class Product {
final String name;
final double price;
const Product(this.name, this.price);
}
void main() {
const apple = Product('Apple', 0.5);
const bread = Product('Bread', 2.25);
final cart = <Product, int>{apple: 6, bread: 2};
var total = 0.0;
cart.forEach((product, qty) {
final line = product.price * qty;
total += line;
print('${product.name.padRight(6)} x$qty ${line.toStringAsFixed(2)}');
});
final count = cart.values.reduce((a, b) => a + b);
print('$count items, total ${total.toStringAsFixed(2)}');
}
// Apple x6 3.00
// Bread x2 4.50
// 8 items, total 7.50接下來可以學什麼
- dart.dev 上的 Dart 語言導覽與教學。
- Effective Dart,官方的風格與設計指南。
- 想開發應用程式的話,可參考 flutter.dev 上的 Flutter 文件。
- pub.dev,Dart 與 Flutter 的套件庫。
常見問題
Dart 只能用在 Flutter 嗎?
不是。Dart 是通用程式語言,可以開發命令列工具、伺服器與網頁應用程式。只是 Flutter 是大家學它最主要的原因。
Dart 是編譯式還是直譯式?
兩種模式都有。開發時,Dart 在支援即時編譯與熱重載的虛擬機器上執行;發行時則預先編譯成原生機器碼,網頁則編譯成 JavaScript 或 WebAssembly。
Dart 的 final 和 const 有什麼差別?
final 變數只指派一次,可以在執行期才決定。const 值必須在編譯期就已知,而且是深層不可變的,這讓 Flutter 能重複使用常數 widget。
Dart 和 Java 或 JavaScript 很像嗎?
它的 C 風格語法與類別,對 Java、C# 和 JavaScript 開發者來說都很熟悉。與 JavaScript 不同的是,Dart 是靜態型別,並具備健全的空值安全。
Dart 的 ?? 運算子有什麼作用?
a ?? b 在 a 不為 null 時回傳 a,否則回傳 b。相關的 a ??= b 只有在 a 為 null 時才會把 b 指派給 a。