for-in, break & continue
for-in, break & continue
Now that you know the basic loops, let's explore Dart's cleaner for-in loop for iterating collections, and two essential control keywords — break (exit the loop early) and continue (skip the current iteration).
🔹 The for-in Loop
The for-in loop is designed specifically for iterating over any iterable — List, Set, Map, String, Range, or custom iterables.
Syntax:
dartfor (var element in iterable) { // use element }
dartvoid main() { List<String> fruits = ['Apple', 'Banana', 'Cherry', 'Mango']; for (var fruit in fruits) { print(fruit); } }
Output:
Apple
Banana
Cherry
Mango
Much cleaner than for (int i = 0; i < fruits.length; i++) when you don't need the index!
Iterating a Set
dartvoid main() { Set<int> primes = {2, 3, 5, 7, 11, 13}; for (var prime in primes) { print('$prime is prime'); } }
Iterating a Map
For a Map, for-in gives you each MapEntry:
dartvoid main() { Map<String, int> scores = { 'Alice': 92, 'Bob': 85, 'Charlie': 78, }; // Iterate entries for (var entry in scores.entries) { print('${entry.key}: ${entry.value}'); } // Iterate only keys for (var name in scores.keys) { print(name); } // Iterate only values for (var score in scores.values) { print(score); } }
Iterating a String
In Dart, a String is an iterable of characters:
dartvoid main() { String word = 'Dart'; for (var char in word.split('')) { print(char); } }
Output:
D
a
r
t
🔹 for-in vs forEach()
Both let you iterate a collection, but they have important differences:
dartvoid main() { List<int> numbers = [1, 2, 3, 4, 5]; // for-in for (var n in numbers) { print(n); } // forEach() with lambda numbers.forEach((n) => print(n)); }
| Aspect | for-in | forEach() |
|---|---|---|
break support | ✅ Yes | ❌ No |
continue support | ✅ Yes | ❌ No |
return exits function | ✅ Yes | ❌ Only exits lambda |
await inside | ✅ Works naturally | ⚠️ Needs asyncForEach workaround |
| Readability | Excellent | Good for simple transforms |
Tip: Prefer for-in over forEach() in most cases. It supports break, continue, and return as expected. Use forEach() only for simple one-liner callbacks.
🔹 break — Exit the Loop
break immediately stops the loop and resumes execution after the closing }.
dartvoid main() { List<int> numbers = [4, 7, 2, 9, 1, 6, 3]; int target = 9; int foundAt = -1; for (int i = 0; i < numbers.length; i++) { if (numbers[i] == target) { foundAt = i; break; // Stop searching — we found it! } } if (foundAt >= 0) { print('Found $target at index $foundAt'); } else { print('$target not found'); } }
Output: Found 9 at index 3
Without break, the loop would continue pointlessly even after finding the target.
break in a while Loop
dartvoid main() { int n = 1; while (true) { // intentional "infinite" loop if (n * n > 100) { break; // exit when n² exceeds 100 } n++; } print('Largest n where n² <= 100: ${n - 1}'); // 10 }
🔹 continue — Skip the Current Iteration
continue skips the rest of the current iteration and jumps to the next one.
dartvoid main() { List<int> numbers = [-3, 7, -1, 5, -8, 2, 4]; print('Positive numbers only:'); for (var n in numbers) { if (n < 0) { continue; // Skip negative numbers } print(n); } }
Output:
Positive numbers only:
7
5
2
4
continue in a for Loop
dartvoid main() { // Print all numbers 1–20, skip multiples of 3 for (int i = 1; i <= 20; i++) { if (i % 3 == 0) { continue; } stdout.write('$i '); } print(''); }
Output: 1 2 4 5 7 8 10 11 13 14 16 17 19 20
🔹 Labels for Nested Loops
When you have nested loops, break and continue by default only affect the innermost loop. Use labels to control an outer loop.
dartvoid main() { // Without label: break only exits inner loop for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (j == 1) break; // only breaks inner loop print('($i, $j)'); } } }
Output:
(0, 0)
(1, 0)
(2, 0)
Labeled break — Exit Outer Loop
dartvoid main() { outerLoop: // label for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (i == 1 && j == 1) { print('Breaking out of both loops at ($i, $j)'); break outerLoop; // breaks the OUTER loop } print('($i, $j)'); } } print('Done.'); }
Output:
(0, 0)
(0, 1)
(0, 2)
(1, 0)
Breaking out of both loops at (1, 1)
Done.
Labeled continue — Skip to Next Outer Iteration
dartvoid main() { outerLoop: for (int i = 1; i <= 3; i++) { for (int j = 1; j <= 3; j++) { if (j == 2) { continue outerLoop; // skip to next i } print('($i, $j)'); } } }
Output:
(1, 1)
(2, 1)
(3, 1)
Note: Labeled breaks are powerful but can make code hard to follow. Consider refactoring into a function with return for cleaner logic.
🔹 Real-World Examples
Example 1: Find First Match
dartString? findFirstAdult(List<Map<String, dynamic>> users) { for (var user in users) { if (user['age'] >= 18) { return user['name']; // return also exits the loop } } return null; } void main() { List<Map<String, dynamic>> users = [ {'name': 'Tom', 'age': 15}, {'name': 'Lisa', 'age': 22}, {'name': 'Mark', 'age': 19}, ]; String? firstAdult = findFirstAdult(users); print('First adult: $firstAdult'); // First adult: Lisa }
Example 2: Skip Negative Numbers, Sum the Rest
dartvoid main() { List<int> data = [10, -3, 25, -8, 4, -1, 17, 6]; int sum = 0; int skipped = 0; for (var n in data) { if (n < 0) { skipped++; continue; } sum += n; } print('Sum of positives: $sum'); // Sum of positives: 62 print('Negative values skipped: $skipped'); // Negative values skipped: 3 }
Example 3: Process Until Condition Met
dartvoid main() { List<String> tasks = [ 'init_database', 'load_config', 'connect_server', 'ERROR_TIMEOUT', // Simulate an error task 'start_app', 'send_report', ]; print('Processing tasks:'); for (var task in tasks) { if (task.startsWith('ERROR')) { print('❌ Error encountered: $task — stopping.'); break; } print('✅ Completed: $task'); } }
Output:
Processing tasks:
✅ Completed: init_database
✅ Completed: load_config
✅ Completed: connect_server
❌ Error encountered: ERROR_TIMEOUT — stopping.
Example 4: Collect Valid Emails Only
dartbool isValidEmail(String email) { return email.contains('@') && email.contains('.'); } void main() { List<String> rawEmails = [ 'alice@example.com', 'invalid-email', 'bob@test.org', 'noatsign', 'charlie@domain.net', ]; List<String> validEmails = []; for (var email in rawEmails) { if (!isValidEmail(email)) { print('Skipping invalid: $email'); continue; } validEmails.add(email); } print('\nValid emails: $validEmails'); }
Output:
Skipping invalid: invalid-email
Skipping invalid: noatsign
Valid emails: [alice@example.com, bob@test.org, charlie@domain.net]
Summary
| Feature | Keyword | Purpose |
|---|---|---|
| For-in loop | for (var x in iterable) | Cleanly iterate any iterable |
| Exit a loop | break | Stop the loop immediately |
| Skip iteration | continue | Skip rest of current iteration, go to next |
| Exit outer loop | break labelName | Break out of a specific outer loop |
| Skip outer iteration | continue labelName | Continue on a specific outer loop |
With for-in, break, and continue in your toolkit, you can write expressive, efficient loops. Up next: functions — the building blocks of reusable code!