串聯語法分為兩種:.. 與 ?..
針對同一個物件連續執行多個屬性指派、方法呼叫,不用宣告暫存變數,程式碼更連貫好讀。
串聯語法並非運算子,是 Dart 專屬的語法糖。
1. 一般串聯 ..
確認物件一定不為 null時使用
所有 .. 的操作都會作用在一開頭的原始物件,忽略每一步方法回傳的數值。
官方示範
import 'dart:ui';
void main() {
// 串聯寫法
var paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0;
// 拆開對等寫法(不使用串聯,需重複變數名稱)
var paint2 = Paint();
paint2.color = Colors.black;
paint2.strokeCap = StrokeCap.round;
paint2.strokeWidth = 5.0;
}Code language: JavaScript (javascript)
這個範例匯入 dart:ui 程式庫,屬於 Flutter 專用程式庫,只能在 Flutter 專案執行。我們現在學習純 Dart,讀者只需看懂語法即可;下方改用純 Dart 內建類別 StringBuffer 示範:
StringBuffer 是專門處理字串操作的類別
void main() {
// 串聯語法示範
var sb = StringBuffer()
..write("Hello ")
..write("FoxDevelop.com ")
..writeln("Cascade");
print(sb.toString());
// 不使用串聯的對等寫法
var sb2 = StringBuffer();
sb2.write("Hello ");
sb2.write("FoxDevelop.com ");
sb2.writeln("Cascade");
print(sb2.toString());
}Code language: PHP (php)
執行輸出
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Hello FoxDevelop.com Cascade
Hello FoxDevelop.com CascadeCode language: CSS (css)
上面的 paint、sb、sb2 這類物件,只要能確保絕對不為 null,就可以用串聯快速指派屬性,不用重複書寫物件名稱呼叫屬性。但如果物件是 null,直接執行會報錯。
void main() {
var sb = null
..write("Hello ")
..write("FoxDevelop.com ")
..writeln("Cascade");
print(sb.toString());
}Code language: JavaScript (javascript)

因此提供第二種寫法來處理空值場景
2. 空值安全串聯 ?..
只要物件是 null,整串串聯鏈全部不執行,避免空指標錯誤;僅串聯開頭使用 ?..,後續直接接 ..
void main() {
var sb = null
?..write("Hello ")
..write("FoxDevelop.com ")
..writeln("Cascade");
print(sb.toString());
}Code language: Dart (dart)
留意第一行是 ?..write(“Hello “),前面多一個問號
重新執行編譯可正常執行,不會拋錯
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
nullCode language: CSS (css)
程式不會報錯
下方為官方示範,讀者認識語法格式即可
// 開頭使用 ?..,後續一律使用 ..
document.querySelector('#confirm')
?..textContent = 'Confirm'
..classList.add('important')
..onClick.listen((e) => window.alert('Confirmed!'))
..scrollIntoView();
// 對等的空安全拆分寫法
final button = document.querySelector('#confirm');
button?.textContent = 'Confirm';
button?.classList.add('important');
button?.onClick.listen((e) => window.alert('Confirmed!'));
button?.scrollIntoView();Code language: JavaScript (javascript)
巢狀串聯
final addressBook = (AddressBookBuilder()
..name = 'jenny'
..email = 'jenny@example.com'
// 內層 PhoneNumberBuilder 獨立使用串聯
..phone = (PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();Code language: PHP (php)
phone 屬性會建立 PhoneNumberBuilder 物件並指派給它。
注意事項
串聯語法只能作用於物件實體,不能接在回傳 void 的方法後,否則會編譯錯誤。
var sb = StringBuffer();
sb.write('foo') // write 方法回傳 void
..write('bar'); // 報錯:void 型別沒有 write 方法Code language: Dart (dart)
這段程式先建立 StringBuffer 物件 sb
這裡用分號結束了 sb.write(‘foo’),而 write 回傳值是 void,後續就不能再接 ..write;只有當方法回傳物件實體時,才能繼續串聯。
正確寫法
var sb = StringBuffer()
..write('foo')
..write('bar');Code language: Dart (dart)
重點整理
..每一次操作都會回到最頂層原始物件,忽略方法回傳值,可連續串接,類似其他程式語言的鏈式呼叫(例如 JavaScript)?..空值安全串聯,避免物件為 null 時發生錯誤,僅需在串聯開頭使用;只要開頭能執行,就代表物件不為 null,後續不需再加問號- 支援巢狀多層串聯
- 不可在回傳
void的方法後使用串聯 ..不屬於運算子,是 Dart 提供的語法糖
何謂語法糖
語法糖是程式語言在解析階段,針對相同底層邏輯提供的簡寫語法;不會新增任何語言功能,編譯器/直譯器產生中間程式碼前,會自動將簡寫還原為標準基礎程式碼。