Dart: Dart Control Flow — Conditional Statements and Loop

Last updated: 2026-08-26

Control flow is the brain of your code — it determines which path your program takes and how many times.

1. What You'll Learn


2. A Developer's Real-World Story

(1) Pain Point: Nested if-else Makes Code Unmaintainable

Alice was processing e-commerce order data and wrote a three-level nested if-else structure to filter and classify orders. The logic was: first check the order status, then check the amount range, and finally check the payment method. A 300-line "pyramid" of code required her to understand all the logic for every modification. One missed change led to an error in VIP order discount calculations, resulting in a direct loss of $15,000 USD.

(2) The Solution: Control Flow Best Practices

Using Dart 3's switch expression to replace the nested if-else, and using for-in to replace index-based iteration reduced the code from 300 lines to 80 lines, making the logic clear and maintainable.

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 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

(3) Benefits


3. if-else Conditional Statements

(1) Basic Syntax

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: if-else Basic Usage

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 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: if-else as Expression (Alternative to Ternary Operator)

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 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
Form Syntax Returns Value Dart Version
if Statement if (cond) { ... } No All versions
Ternary Operator cond ? a : b Yes All versions
if Expression if (cond) a else b Yes Dart 3.7+

4. switch and switch Expressions

(1) Traditional switch Statement

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: switch Statement

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 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

(2) Dart 3 switch Expressions

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: switch Expression

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 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
Aspect switch Statement switch Expression
Returns Value No Yes
break Required Not needed
Syntax case x: x =>
Dart Version All Dart 3+
Exhaustiveness Check No Yes (sealed class)

5. Loop Structures

(1) for Loop

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: Traditional for Loop

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 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

(2) for-in Loop

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: for-in Traversal

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 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

(3) while and do-while Loops

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: while and 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 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
Loop Type Initial Check Minimum Executions Use Case
for Before entry 0 Known number of times
for-in Before entry 0 Traversing collections
while Before entry 0 Condition-driven
do-while After execution 1 Must execute at least once

6. break, continue, and Labels

(1) break and continue

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: break and 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 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: Labeled Control

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 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

7. Bob's Scenario: DataPipeline Data Filtering

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 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: Order Data Filtering and Aggregation

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 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

8. Complete Example: DataPipeline Batch Processing Controller

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 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

Output:

TEXT 📖 Display only
--- 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

❓ FAQ

Q: Is break mandatory in switch statements? A: Yes, in Dart's switch statement, every non-empty case must end with a break, return, throw, or continue. It does not fall through. However, an empty case can fall through.

Q: What is the _ in a switch expression? A: _ is a wildcard pattern that matches all remaining cases, equivalent to the default in a switch statement. Dart 3 recommends using _ instead of default.

Q: Can for-in modify a collection? A: No. Modifying a collection (adding/removing elements) during a for-in traversal will cause a ConcurrentModificationError. If modification is needed, collect the changes first and apply them afterward.

Q: When should I use while instead of for? A: Use while when the number of iterations is uncertain (e.g., waiting for a network response). Use for/for-in when the number of iterations is known or there is a clear collection to traverse.

Q: Are labels commonly used in actual development? A: Not often. Labeled break/continue is mainly used for precise control in nested loops. In most cases, deep nesting can be avoided through refactoring (extracting methods, using higher-order functions).

Q: Does Dart 3's switch expression support multi-value matching? A: Yes. You can use | to combine multiple patterns, like 'pending' | 'processing' => 'In Progress'.

Q: Is there a performance difference between do-while and while? A: There is no perceptible performance difference. The choice depends on semantics: whether the loop body needs to execute at least once.


📖 Summary


📝 Exercises

  1. Basic (Difficulty ⭐): Use for-in to traverse a List containing 5 order amounts. Calculate the total and average, skipping orders with an amount less than or equal to 0.
  2. Intermediate (Difficulty ⭐⭐): Use a switch expression to implement an order status classifier that returns different processing priority labels based on the combination of (status, amount).
  3. Challenge (Difficulty ⭐⭐⭐): Implement a paginated batch processor that processes N records per batch, supports break interruption (on encountering a severe error) and continue skipping (for invalid records), and outputs a final processing statistics report.

← Previous Lesson | Next Lesson →

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

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

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