Dart: Dart 控制流 — 条件判断与循环结构
控制流是代码的大脑 — 它决定程序走哪条路、走多少遍。
1. 你将学到
- if-else / switch-case(含 Dart 3 switch 表达式预览)
- for / while / do-while 循环
- for-in 与 Iterable 遍历
- break / continue 与标签控制
- Bob 场景:DataPipeline 数据过滤逻辑
2. 一个开发者的真实故事
(1) 痛点:嵌套 if-else 让代码无法维护
Alice 在处理电商订单数据时,写了一段三层嵌套的 if-else 来过滤和分类订单。代码逻辑是:先判断订单状态,再判断金额区间,最后判断支付方式。300 行代码的"金字塔"让她每次修改都要理解全部逻辑,一次漏改导致 VIP 订单折扣计算错误,直接损失 15,000 USD。
(2) 控制流最佳实践的解法
用 Dart 3 的 switch 表达式替代嵌套 if-else,用 for-in 替代索引遍历,代码从 300 行缩减到 80 行,逻辑清晰可维护。
// Before: nested if-else pyramid
// After: clean switch expression
String classifyOrder(Order order) => switch ((order.status, order.amount)) {
('pending', > 1000) => 'VIP Pending',
('pending', _) => 'Normal Pending',
('shipped', _) => 'In Transit',
('delivered', _) => 'Completed',
_ => 'Unknown',
};
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
(3) 收益
- switch 表达式让分支逻辑一目了然,不再有嵌套金字塔
- for-in 遍历更安全,避免越界错误
- 标签控制让复杂循环的 break/continue 精准跳转
3. if-else 条件判断
(1) 基础语法
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:if-else 基本用法
void main() {
double orderAmount = 1500.0;
if (orderAmount > 1000) {
print('VIP order - apply discount');
} else if (orderAmount > 500) {
print('Standard order');
} else {
print('Small order');
}
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:if-else 表达式(三元运算的替代)
void main() {
int orderCount = 1500;
// Ternary operator
String tier = orderCount >= 1000 ? 'Enterprise' : 'Standard';
// if-else as expression (Dart 3)
String label = if (orderCount >= 1000) 'Enterprise' else 'Standard';
print('Tier: $tier');
print('Label: $label');
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
| 形式 | 语法 | 返回值 | Dart 版本 |
|---|---|---|---|
| if 语句 | if (cond) { ... } |
无 | 所有版本 |
| 三元运算 | cond ? a : b |
有 | 所有版本 |
| if 表达式 | if (cond) a else b |
有 | Dart 3.7+ |
4. switch 与 switch 表达式
(1) 传统 switch 语句
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:switch 语句
void main() {
String status = 'shipped';
switch (status) {
case 'pending':
print('Order is waiting for processing');
break;
case 'shipped':
print('Order is in transit');
break;
case 'delivered':
print('Order has been delivered');
break;
default:
print('Unknown status: $status');
}
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
(2) Dart 3 switch 表达式
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:switch 表达式
// Switch expression - returns a value
String getStatusLabel(String status) => switch (status) {
'pending' => 'Awaiting Processing',
'shipped' => 'In Transit',
'delivered' => 'Completed',
'cancelled' => 'Cancelled',
_ => 'Unknown Status',
};
// Pattern matching with guards
String classifyOrder(double amount) => switch (amount) {
>= 10000 => 'Enterprise',
>= 1000 => 'Premium',
> 0 => 'Standard',
_ => 'Invalid',
};
void main() {
print(getStatusLabel('shipped')); // In Transit
print(classifyOrder(5500)); // Premium
print(classifyOrder(150)); // Standard
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
| 维度 | switch 语句 | switch 表达式 |
|---|---|---|
| 返回值 | 无 | 有 |
| break | 必须 | 不需要 |
| 语法 | case x: |
x => |
| Dart 版本 | 所有 | Dart 3+ |
| 穷尽检查 | 无 | 有(sealed class) |
5. 循环结构
(1) for 循环
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:传统 for 循环
void main() {
// Classic for loop
for (int i = 0; i < 5; i++) {
print('Processing batch ${i + 1}');
}
// Count down
for (int i = 10; i > 0; i -= 2) {
print('Countdown: $i');
}
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
(2) for-in 循环
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:for-in 遍历
void main() {
List``<String>`` sources = ['orders.csv', 'products.csv', 'customers.csv'];
// for-in with List
for (final source in sources) {
print('Loading: $source');
}
// for-in with Set
Set``<String>`` uniqueStatuses = {'pending', 'shipped', 'delivered'};
for (final status in uniqueStatuses) {
print('Status: $status');
}
// for-in with Map entries
Map<String, int> categoryCount = {
'Electronics': 500,
'Clothing': 300,
'Books': 200,
};
for (final entry in categoryCount.entries) {
print('${entry.key}: ${entry.value} products');
}
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
(3) while 和 do-while 循环
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:while 与 do-while
void main() {
// while - check condition first
int retries = 0;
bool connected = false;
while (!connected && retries < 3) {
print('Connection attempt ${retries + 1}...');
retries++;
connected = retries >= 2; // Simulated success
}
// do-while - execute at least once
int batchSize = 10000;
int processed = 0;
do {
int chunk = (batchSize - processed > 1000) ? 1000 : batchSize - processed;
print('Processing $chunk records...');
processed += chunk;
} while (processed < batchSize);
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
| 循环类型 | 首次检查 | 最少执行次数 | 适用场景 |
|---|---|---|---|
for |
进入前 | 0 | 已知次数 |
for-in |
进入前 | 0 | 遍历集合 |
while |
进入前 | 0 | 条件驱动 |
do-while |
执行后 | 1 | 至少执行一次 |
6. break、continue 与标签
(1) break 与 continue
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:break 和 continue
void main() {
List``<double>`` amounts = [1500, -50, 3200, 0, 890, -100, 2100];
// continue - skip invalid orders
int validCount = 0;
double totalValid = 0;
for (final amount in amounts) {
if (amount <= 0) continue; // Skip invalid
validCount++;
totalValid += amount;
}
print('Valid: $validCount, Total: $totalValid USD');
// break - stop at first error
for (final amount in amounts) {
if (amount < 0) {
print('Error: Negative amount $amount found!');
break;
}
print('Processing: $amount USD');
}
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:标签控制
void main() {
// Label for nested loop control
outer:
for (int batch = 0; batch < 3; batch++) {
for (int record = 0; record < 5; record++) {
if (record == 2 && batch == 1) {
print('Critical error at batch $batch, record $record');
break outer; // Break out of both loops
}
print('Batch $batch, Record $record');
}
}
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
7. Bob 场景:DataPipeline 数据过滤
flowchart TD
A[Raw Data] --> B{Filter Condition}
B -->|Valid Order| C[for-in Iterate]
B -->|Invalid Data| D[continue Skip]
C --> E[Aggregate Calculation]
E --> F[Output Result]
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
▶ 示例
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
:订单数据过滤与聚合
void main() {
List<Map<String, dynamic>> rawOrders = [
{'id': 'ORD-001', 'amount': 1500.0, 'status': 'completed'},
{'id': 'ORD-002', 'amount': -50.0, 'status': 'completed'},
{'id': 'ORD-003', 'amount': 3200.0, 'status': 'pending'},
{'id': 'ORD-004', 'amount': 0.0, 'status': 'completed'},
{'id': 'ORD-005', 'amount': 890.0, 'status': 'completed'},
{'id': 'ORD-006', 'amount': 2100.0, 'status': 'cancelled'},
];
double totalRevenue = 0;
int completedCount = 0;
Map<String, double> revenueByStatus = {};
for (final order in rawOrders) {
final amount = order['amount'] as double;
final status = order['status'] as String;
// Skip invalid orders
if (amount <= 0) continue;
// Skip cancelled orders
if (status == 'cancelled') continue;
// Aggregate revenue by status
revenueByStatus[status] = (revenueByStatus[status] ?? 0) + amount;
// Count completed
if (status == 'completed') {
completedCount++;
totalRevenue += amount;
}
}
print('=== DataPipeline Filter Report ===');
print('Completed orders: $completedCount');
print('Total revenue: \$${totalRevenue.toStringAsFixed(2)} USD');
for (final entry in revenueByStatus.entries) {
print(' ${entry.key}: \$${entry.value.toStringAsFixed(2)} USD');
}
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
8. 完整示例:DataPipeline 批处理控制器
// ============================================
// DataPipeline Batch Processing Controller
// Uses all control flow structures
// ============================================
class BatchProcessor {
final int batchSize;
int processedCount = 0;
int skippedCount = 0;
double totalRevenue = 0;
BatchProcessor({this.batchSize = 1000});
String classifyAmount(double amount) => switch (amount) {
>= 10000 => 'Enterprise',
>= 1000 => 'Premium',
> 0 => 'Standard',
_ => 'Invalid',
};
void processBatch(List<Map<String, dynamic>> orders) {
int batchNum = 0;
for (int i = 0; i < orders.length; i += batchSize) {
batchNum++;
final end = (i + batchSize < orders.length) ? i + batchSize : orders.length;
final batch = orders.sublist(i, end);
print('\n--- Batch $batchNum (${batch.length} records) ---');
for (final order in batch) {
final id = order['id'] as String?;
final amount = order['amount'] as double?;
final status = order['status'] as String?;
// Skip invalid records
if (id == null || amount == null || status == null) {
skippedCount++;
continue;
}
if (amount <= 0) {
skippedCount++;
continue;
}
final tier = classifyAmount(amount);
if (status == 'completed') {
processedCount++;
totalRevenue += amount;
print(' $id: \$${amount.toStringAsFixed(2)} USD [$tier]');
} else if (status == 'pending') {
print(' $id: PENDING - \$${amount.toStringAsFixed(2)} USD [$tier]');
} else {
skippedCount++;
}
}
}
}
void printSummary() {
print('\n=== Processing Summary ===');
print('Processed: $processedCount orders');
print('Skipped: $skippedCount records');
print('Revenue: \$${totalRevenue.toStringAsFixed(2)} USD');
}
}
void main() {
final orders = <Map<String, dynamic>>[
{'id': 'ORD-001', 'amount': 1500.0, 'status': 'completed'},
{'id': 'ORD-002', 'amount': 15500.0, 'status': 'completed'},
{'id': 'ORD-003', 'amount': -50.0, 'status': 'completed'},
{'id': 'ORD-004', 'amount': 890.0, 'status': 'pending'},
{'id': 'ORD-005', 'amount': 3200.0, 'status': 'completed'},
{'id': 'ORD-006', 'amount': 0.0, 'status': 'cancelled'},
];
final processor = BatchProcessor(batchSize: 3);
processor.processBatch(orders);
processor.printSummary();
}
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
输出:
--- Batch 1 (3 records) ---
ORD-001: $1500.00 USD [Premium]
ORD-002: $15500.00 USD [Enterprise]
--- Batch 2 (3 records) ---
ORD-004: PENDING - $890.00 USD [Standard]
ORD-005: $3200.00 USD [Premium]
=== Processing Summary ===
Processed: 3 orders
Skipped: 3 records
Revenue: $20200.00 USD
❓ 常见问题
Q:switch 语句必须加 break 吗? A:是的,Dart 的 switch 语句每个非空 case 必须以 break、return、throw 或 continue 结束,不会 fall-through。但空的 case 可以 fall-through。
Q:switch 表达式的
_是什么? A:_是通配符模式(wildcard pattern),匹配所有剩余情况,相当于 switch 语句的 default。Dart 3 推荐用_而非 default。
Q:for-in 可以修改集合吗? A:不可以。在 for-in 遍历过程中修改集合(添加/删除元素)会导致 ConcurrentModificationError。需要修改时,先收集修改再统一应用。
Q:什么时候用 while 而不是 for? A:循环次数不确定时用 while(如等待网络响应);循环次数已知或有明确集合时用 for/for-in。
Q:标签控制在实际开发中常用吗? A:不常用。标签 break/continue 主要用于嵌套循环的精确控制。大多数场景可以通过重构(提取方法、使用 higher-order 函数)避免深层嵌套。
Q:Dart 3 的 switch 表达式支持多值匹配吗? A:支持。可以用 | 组合多个模式,如
'pending' | 'processing' => 'In Progress'。
Q:do-while 和 while 的性能有区别吗? A:没有可感知的性能差异。选择取决于语义:是否需要至少执行一次循环体。
📖 小节
- if-else 是最基本的条件判断,Dart 3 支持 if 表达式语法
- switch 表达式(Dart 3)比 switch 语句更简洁、更安全,支持穷尽检查
- 四种循环各有适用场景:for(已知次数)、for-in(遍历集合)、while(条件驱动)、do-while(至少一次)
- break 跳出循环,continue 跳过当前迭代,标签让嵌套循环的跳转更精准
- DataPipeline 用 continue 过滤无效数据,switch 表达式分类订单等级
📝 作业
- 基础题(难度⭐):用 for-in 遍历一个包含 5 个订单金额的 List,计算总和与平均值,跳过金额小于等于 0 的订单。
- 进阶题(难度⭐⭐):用 switch 表达式实现一个订单状态分类器,根据 (status, amount) 的组合返回不同的处理优先级标签。
- 挑战题(难度⭐⭐⭐):实现一个分页批处理器,每批处理 N 条记录,支持 break 中断(遇到严重错误)和 continue 跳过(跳过无效记录),最后输出处理统计报告。