在 Dart 語言中,函式本身也是物件,這一點和其他程式語言略有不同。
函式屬於一級物件
Dart 是純粹的物件導向語言,函式本身就是物件,型別為 Function。
可指派給變數、做為參數傳入其他函式;
類別實體可以直接像函式一樣呼叫
1 標準函式定義
// 完整標註回傳值、參數型別
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
Code language: Dart (dart)
完整範例
// 稀有氣體原子序對照表
final Map<int, String> _nobleGases = {
2: 'He',
10: 'Ne',
18: 'Ar',
36: 'Kr',
54: 'Xe',
86: 'Rn',
};
// 完整標註回傳值、參數型別
// int 不可為空,無法傳入 null,改為 int? 支援可空參數
bool isNoble(int? atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
void main() {
print(isNoble(null)); // null 不在 Map 鍵值內 → false
print(isNoble(2)); // 存在稀有氣體 → true
print(isNoble(9)); // 不存在 → false
print(isNoble(86)); // 存在 → true
}
Code language: Dart (dart)
false
true
false
trueCode language: JavaScript (javascript)
2 省略型別標註
Effective Dart 程式碼規範建議,對外開放的公開介面(公開 API)必須完整手動標註型別。
如果是內部使用的函式,則可以省略型別
範例
final Map<int, String> _nobleGases = {
2: 'He',
10: 'Ne',
18: 'Ar',
36: 'Kr',
54: 'Xe',
86: 'Rn',
};
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
void main() {
print(isNoble(null)); // null 不在 Map 鍵值內 → false
print(isNoble(2)); // 存在稀有氣體 → true
print(isNoble(9)); // 不存在 → false
print(isNoble(86)); // 存在 → true
}
Code language: Dart (dart)
上面範例的 isNoble 函式,移除了回傳型別 bool,參數也省略 int? 型別,依舊可以正常執行。
當你沒有撰寫型別時,Dart 會自動推斷型別:
- 參數
atomicNumber:無顯式型別 → 推斷型別為dynamic(動態型別,任意值皆可傳入:數字、字串、null 都允許) - 回傳值:前方未標註型別 → Dart 依據
return右側運算式推斷回傳型別為bool,只是程式碼不會顯示標註
為什麼公開 API 一定要標註型別?
可讀性:其他開發者呼叫你的函式時,一眼就能看懂參數、回傳值的型別,不用閱讀內部實作猜測;
型別安全:dynamic 動態型別會關閉靜態檢查,傳入錯誤型別不會觸發編譯錯誤,容易造成上線後崩潰;
IDE 提示:標註型別後編輯器會自動補全、顯示參數提示;若無型別標註,IDE 無法精準提示。
什麼時候可以省略型別?
僅建議用於區域私有程式碼,函式只在當前檔案內使用、不會對外被呼叫。
箭頭簡寫語法(=>)
若函式主體(大括號 {} 內的程式碼)只有單一行回傳運算式,可簡寫為箭頭函式。
範例
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
Code language: Dart (dart)
=> expr等同於{ return expr; };- 箭頭與分號之間只能撰寫運算式,不能撰寫陳述式(例如
if,可改用三元運算式替代)。
// 稀有氣體原子序對照表
final Map<int, String> _nobleGases = {
2: '氦',
10: '氖',
18: '氬',
36: '氪',
54: '氙',
86: '氡',
};
// 箭頭簡寫函式:單行運算式直接回傳,等同帶有 return 的區塊函式
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
void main() {
// 測試存在的稀有氣體
print(isNoble(2)); // true
print(isNoble(18)); // true
// 測試一般元素
print(isNoble(6)); // false(碳)
print(isNoble(99)); // false(無對應元素)
}
Code language: Dart (dart)

Parameters 參數
函式支援必要位置參數,其後二選一使用:具名參數 / 選擇性位置參數,兩者不可混用。
定義或呼叫參數時允許尾隨逗號 ,,提升程式碼可讀性。
1. 具名參數 Named parameters
使用 {} 包裹,預設為選填;加上 required 強制必須傳入。
1)基礎可空具名參數(無預設值,預設為 null,型別必須標註可空 ?)
void enableFlags({bool? bold, bool? hidden}) {}
// 呼叫時明確指定參數名稱
enableFlags(bold: true, hidden: false);
Code language: Dart (dart)
完整範例
/// 開啟文字樣式標記:粗體、隱藏
void enableFlags({bool? bold, bool? hidden}) {
print('粗體:$bold,隱藏:$hidden');
}
void main() {
// 完整傳入所有具名參數
enableFlags(bold: true, hidden: false);
// 只傳其中一個具名參數,另一個為 null
enableFlags(bold: true);
// 只傳 hidden
enableFlags(hidden: true);
// 不傳任何參數,兩個值都是 null
enableFlags();
}
Code language: Dart (dart)
執行結果
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
粗體:true,隱藏:false
粗體:true,隱藏:null
粗體:null,隱藏:true
粗體:null,隱藏:nullCode language: JavaScript (javascript)
2)設定編譯期常數預設值(不需可空標註,已有預設值故不用標註 ?)
void enableFlags({bool bold = false, bool hidden = false}) {}
enableFlags(bold: true); // hidden 自動使用預設 false
Code language: Dart (dart)
範例
/// 開啟文字樣式標記:粗體、隱藏
void enableFlags({bool bold = false, bool hidden = false}) {
print('粗體:$bold,隱藏:$hidden');
}
void main() {
enableFlags();//不傳參數,兩者使用預設 false → 粗體:false,隱藏:false
}
Code language: Dart (dart)
3)強制必填具名參數 required(備註:Flutter 元件建構子經常使用)以下範例為 Flutter 程式,可至 Flutter 課程深入學習。
const Scrollbar({super.key, required Widget child});
// required 參數也支援可空型別
const Scrollbar({super.key, required Widget? child});
Code language: Dart (dart)
Dart 不強制具名參數必須放在參數列表最後,可穿插在任意位置
repeat(times: 2, () {})Code language: CSS (css)
2. 選擇性位置參數 Optional positional parameters
使用 [] 包裹,未傳入時預設為 null,可設定常數預設值。
// 無預設值,型別標註 ?
String say(String from, String msg, [String? device]) {
var result = '$from says $msg';
if (device != null) result = '$result with a $device';
return result;
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
assert(say('Bob', 'Howdy', 'smoke signal') == 'Bob says Howdy with a smoke signal');
Code language: Dart (dart)
完整範例
// [String? device] 選擇性位置參數,無預設值,未傳入則為 null
String say(String from, String msg, [String? device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device'; //若 device 不為 null,就在訊息後追加裝置資訊
}
return result;
}
void main() {
// 不傳入第三個選擇性參數
String res1 = say('Bob', 'Howdy');
print(res1);
// 傳入第三個選擇性位置參數
String res2 = say('Bob', 'Howdy', 'smoke signal');
print(res2);
// 手動傳入 null 給選擇性參數
String res3 = say('Alice', 'Hi', null);
print(res3);
}
Code language: Dart (dart)
設定預設值:
String say(String from, String msg, [String device = 'carrier pigeon']) {
return '$from says $msg with a $device';
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon');
Code language: Dart (dart)
完整範例
// 選擇性位置參數,設定預設值 carrier pigeon(信鴿)
String say(String from, String msg, [String device = 'carrier pigeon']) {
return '$from says $msg with a $device';
}
void main() {
// 不傳入第三個選擇性參數,自動使用預設值 carrier pigeon
String res1 = say('Bob', 'Howdy');
print(res1); //Bob says Howdy with a carrier pigeon
// 手動傳入自訂裝置,覆蓋預設值
String res2 = say('Bob', 'Howdy', 'smoke signal');
print(res2); //Bob says Howdy with a smoke signal
String res3 = say('Alice', 'Hello', 'mobile phone');
print(res3); // Alice says Hello with a mobile phone
String res4 = say('Jack', 'Tom', 'website foxdevelop.com');
print(res4); // Jack says Tom with a website foxdevelop.com
}
Code language: Dart (dart)
執行結果
Built firstdart:firstdart.
Bob says Howdy with a carrier pigeon
Bob says Howdy with a smoke signal
Alice says Hello with a mobile phone
Jack says Tom with a website foxdevelop.comCode language: JavaScript (javascript)
main () 進入點函式
所有 Dart 程式都必須存在頂層 main(),是程式唯一執行入口。
- 無參數基礎版本
void main() {
print('Hello, welcome to foxdevelop.com!');
}
Code language: Dart (dart)
Hello, welcome to foxdevelop.com!Code language: CSS (css)
- 命令列程式接收啟動參數
// 執行指令:dart run firstdart.dart 1 test
void main(List<String> arguments) {
print(arguments); // [1, test]
}
Code language: Dart (dart)
複雜命令列參數解析可使用官方 args 函式庫。
上方的 firstdart.dart 請替換成你的 Dart 檔案名稱

函式做為一級物件 First-class objects
1. 將函式做為參數傳遞
void printElement(int element) => print(element);
var list = [1, 2, 3];
list.forEach(printElement); // 直接傳入函式
Code language: Dart (dart)
完整範例
void main() {
void printElement(int element) => print(element);
var list = [1, 2, 3];
list.forEach(printElement); // 直接傳入函式
}
Code language: Dart (dart)
輸出
1
2
3
2. 匿名函式指派給變數
void main() {
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
print(loudify('hello')); //!!! HELLO !!!
}
Code language: Dart (dart)
函式型別 Function Type
可顯式宣告函式變數的型別
格式:回傳值型別 Function(參數列表, {具名參數})
void greet(String name, {String greeting = 'Hello'}) => print('$greeting $name!');
// 定義函式型別變數
void Function(String, {String greeting}) g = greet;
g('Dash', greeting: 'Howdy');
Code language: Dart (dart)
可透過 typedef 替函式型別建立別名,提升重複使用性與可讀性。
{String greeting}) → 代表一個具名參數 greeting,型別為 String
void main() {
void greet(String name, {String greeting = 'Hello'}) => print('$greeting $name!');
// 定義函式型別變數
void Function(String, {String greeting}) g = greet;
g('Jack', greeting: 'Howdy');
}
Code language: Dart (dart)
輸出
Howdy Jack!
匿名函式(Lambda / Closure 閉包)
沒有函式名稱的函式,有兩種寫法:
- 區塊寫法(多行)
void main() {
const list = ['apples', 'bananas'];
var uppercaseList = list.map((item) {
return item.toUpperCase();
}).toList();
print(uppercaseList);
}
Code language: Dart (dart)
上方程式碼中的 (item) { return item.toUpperCase(); } 即為匿名函式(lambda)
輸出:[APPLES, BANANAS]
- 箭頭簡寫(單行運算式)
void main() {
const list = ['fox', 'develop','foxdevelop.com'];
var uppercaseList = list.map((item) => item.toUpperCase()).toList();
uppercaseList.forEach((item) => print('$item: ${item.length}'));
}
Code language: Dart (dart)
輸出
FOX: 3
DEVELOP: 7
FOXDEVELOP.COM: 14Code language: HTTP (http)
詞法作用域 Lexical scope
Dart 採用靜態詞法作用域:變數作用域由程式大括號層級決定,內層可存取所有外層變數。
bool topLevel = true;
void main() {
var insideMain = true;
void myFunction() {
var insideFunction = true;
void nestedFunction() {
var insideNestedFunction = true;
// 全部皆可存取
assert(topLevel && insideMain && insideFunction && insideNestedFunction);
}
}
}
Code language: Dart (dart)
詞法閉包 Lexical closures
閉包:函式物件可以捕獲並持有外層作用域變數,脫離原本作用域後仍可正常存取。
Function makeAdder(int addBy) {
// 捕獲外部變數 addBy
return (int i) => addBy + i;
}
void main() {
var add2 = makeAdder(2);
var add4 = makeAdder(4);
assert(add2(3) == 5);
assert(add4(3) == 7);
}
Code language: Dart (dart)
以下逐步解析上方程式執行流程
第一步:var add2 = makeAdder(2);
- 執行
makeAdder,傳入addBy = 2 - 建立匿名函式:
(int i) => 2 + i - 將此函式指派給變數
add2,此時 add2 本身就是函式物件,函式主體為(int i) => 2 + i,接收 int i 並回傳 i+2 makeAdder函式執行完畢,但匿名函式會永久記住addBy=2
第二步:var add4 = makeAdder(4);
- 再次執行
makeAdder,傳入addBy = 4 - 建立完全獨立全新的匿名函式:
(int i) => 4 + i - 指派給
add4,它單獨保存一份addBy=4,與 add2 互不干擾;也就是 addBy 在兩個函式物件 add2、add4 內各自獨立儲存,一個存 2、一個存 4
- 執行
makeAdder(2)時,會產生獨立閉包,單獨綁定一份addBy = 2,指派給add2; - 執行
makeAdder(4)時,產生另一個全新閉包,單獨綁定一份addBy = 4,指派給add4; add2與add4是兩個完全互不影響的函式物件,各自私有存放捕獲的addBy,彼此不會互相干擾。
第三步:呼叫 add2(3)
執行內部函式:2 + 3 = 5,斷言成立
第四步:呼叫 add4(3)
執行內部函式:4 + 3 = 7,斷言成立
函式截取 Tear-offs
呼叫函式 / 方法時不加括號,會產生 tear-off(輕量閉包),取代手寫匿名 lambda,程式碼更簡潔。
推薦寫法,直接傳入函式即可。
var charCodes = [68, 97, 114, 116];
var buffer = StringBuffer();
charCodes.forEach(print); // 函式截取
charCodes.forEach(buffer.write); // 方法截取
Code language: Dart (dart)
冗餘寫法
charCodes.forEach((code) => print(code));
charCodes.forEach((code) => buffer.write(code));
Code language: Dart (dart)
函式相等性判斷
- 頂層函式、靜態方法:函式名完全相同則
==判斷為相等; - 實例方法:只有綁定同一個物件實體的方法截取才會判定相等。
void foo() {}
class A {
static void bar() {}
void baz() {}
}
void main() {
var x = foo;
assert(foo == x);
x = A.bar;
assert(A.bar == x);
var v = A(), w = A(), y = w;
x = w.baz;
assert(y.baz == x); // 同一實體,相等
assert(v.baz != w.baz); // 不同實體,不相等
}
Code language: Dart (dart)
回傳值 Return values
- 所有函式必有回傳值:若沒有顯式撰寫
return,程式末尾會自動隱藏追加return null;
foo() {}
assert(foo() == null);
Code language: Dart (dart)
- 回傳多個值:使用 Record 紀錄元組
(String, int) foo() {
return ('something', 42);
}
Code language: Dart (dart)
Getter & Setter 存取器
所有屬性讀取 / 賦值本質都是呼叫 getter /setter;一般變數會自動產生隱藏存取器,也可手動明確定義。
優勢:外部統一使用 .屬性名 存取,內部邏輯可隨時修改,不用異動外部呼叫程式。
String _secret = 'Hello';
// Getter 讀取專用
String get secret {
print('呼叫getter');
return _secret.toUpperCase();
}
// Setter 賦值專用
set secret(String newMsg) {
print('呼叫setter');
if (newMsg.isNotEmpty) _secret = newMsg;
}
void main() {
print(secret); // 讀取,觸發getter → HELLO
secret = 'Dart is fun'; // 賦值,觸發setter
print(secret); // DART IS FUN
}
Code language: Dart (dart)
* 產生器 Generators
延遲按需產生序列,分同步、非同步兩種,使用 yield 輸出數值。
- 同步產生器
sync*,回傳Iterable
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
Code language: Dart (dart)
- 非同步產生器
async*,回傳Stream
Stream<int> asyncNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
Code language: Dart (dart)
- 遞迴產生器最佳化
yield*(轉發另一個產生器,提升執行效能)
Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
Code language: Dart (dart)