Dart: Dart 控制流 — 条件判断与循环结构

控制流是代码的大脑 — 它决定程序走哪条路、走多少遍。

1. 你将学到


2. 一个开发者的真实故事

(1) 痛点:嵌套 if-else 让代码无法维护

Alice 在处理电商订单数据时,写了一段三层嵌套的 if-else 来过滤和分类订单。代码逻辑是:先判断订单状态,再判断金额区间,最后判断支付方式。300 行代码的"金字塔"让她每次修改都要理解全部逻辑,一次漏改导致 VIP 订单折扣计算错误,直接损失 15,000 USD。

(2) 控制流最佳实践的解法

用 Dart 3 的 switch 表达式替代嵌套 if-else,用 for-in 替代索引遍历,代码从 300 行缩减到 80 行,逻辑清晰可维护。

DART
// 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',
};
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

(3) 收益


3. if-else 条件判断

(1) 基础语法

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:if-else 基本用法

DART
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');
  }
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:if-else 表达式(三元运算的替代)

DART
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');
}
TEXT 📖 仅展示
> **输出:** 在本地 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 语句

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:switch 语句

DART
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');
  }
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

(2) Dart 3 switch 表达式

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:switch 表达式

DART
// 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
}
TEXT 📖 仅展示
> **输出:** 在本地 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 循环

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:传统 for 循环

DART
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');
  }
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

(2) for-in 循环

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:for-in 遍历

DART
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');
  }
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

(3) while 和 do-while 循环

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:while 与 do-while

DART
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);
}
TEXT 📖 仅展示
> **输出:** 在本地 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

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:break 和 continue

DART
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');
  }
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:标签控制

DART
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');
    }
  }
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

7. Bob 场景:DataPipeline 数据过滤

100%
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]
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:订单数据过滤与聚合

DART
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');
  }
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

8. 完整示例:DataPipeline 批处理控制器

DART
// ============================================
// 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();
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

输出:

TEXT 📖 仅展示
--- 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:没有可感知的性能差异。选择取决于语义:是否需要至少执行一次循环体。


📖 小节


📝 作业

  1. 基础题(难度⭐):用 for-in 遍历一个包含 5 个订单金额的 List,计算总和与平均值,跳过金额小于等于 0 的订单。
  2. 进阶题(难度⭐⭐):用 switch 表达式实现一个订单状态分类器,根据 (status, amount) 的组合返回不同的处理优先级标签。
  3. 挑战题(难度⭐⭐⭐):实现一个分页批处理器,每批处理 N 条记录,支持 break 中断(遇到严重错误)和 continue 跳过(跳过无效记录),最后输出处理统计报告。

← 上一课 | 下一课 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏