Dart 11 ๐ฏ Loops: for, for-in, while, do-while, and Loop Control (break, continue)
Loops repeat a block of code. Dart provides four loop constructs โ for, for-in, while, and do-while โ each suited to a different situation. The classic for loop is for counting and index-based iteration. The for-in loop iterates over the elements of a collection. The while loop repeats while a condition is true. The do-while loop executes the body at least once before checking the condition. Loop control statements โ break and continue โ exit a loop or skip an iteration, and labeled loops allow control flow across nested loops.
The choice among the four constructs is not arbitrary. for-in is the idiomatic way to iterate over a list, set, or map in Dart, and it eliminates the off-by-one errors that come with index-based loops. The classic for loop is appropriate when the index matters or when the iteration count is known. while is for condition-driven repetition. do-while is for cases where the body must run at least once, such as prompting for input until a valid value is entered.
This chapter covers each loop construct, the difference between them, loop control with break and continue, labeled loops for nested iteration, the forEach method as an alternative for collections, and the patterns that make iteration readable and correct.
Key point: Use for-in to iterate over collections, the classic for when the index matters, while for condition-driven repetition, and do-while when the body must run at least once. break exits a loop, continue skips to the next iteration, and labels allow both to target an outer loop. Dart’s for-in over a collection is preferred over index-based iteration.
Why multiple loop constructs exist
The counting problem. Some loops run a known number of times: process the first ten items, generate a multiplication table, iterate from 0 to n. The classic for loop with an index expresses this directly, and the index is available for computations.
The collection problem. Most iteration is over a collection: a list of users, a set of tags, a map of settings. The for-in loop iterates over the elements directly, without an index, and it works with any object that implements Iterable. This is the idiomatic form for collections.
The condition problem. Some loops repeat until a condition changes: read until end of file, retry until success, drain a queue. The while loop checks the condition before each iteration, so the body may run zero times if the condition is initially false.
The at-least-once problem. Some loops must run the body at least once before checking the condition: prompt for input, then validate; attempt an operation, then check success. The do-while loop checks the condition after the body, guaranteeing at least one execution.
The control problem. Inside a loop, it is sometimes necessary to exit early or skip an iteration. break and continue provide this, and labels extend it to nested loops so that an inner loop can break out of an outer one.
a. The classic for loop
The classic for loop has three parts in parentheses: an initializer, a condition, and an update.
for (var i = 0; i < 5; i++) {
print(i); // 0, 1, 2, 3, 4
}
The initializer runs once before the loop begins. The condition is checked before each iteration. The update runs after each iteration. When the condition is false, the loop ends.
The index variable is available inside the body, so the classic for is appropriate when the index matters:
var names = ['Alice', 'Bob', 'Carol'];
for (var i = 0; i < names.length; i++) {
print('${i + 1}. ${names[i]}'); // 1. Alice, 2. Bob, 3. Carol
}
The loop can count in any direction or by any step:
// Count down
for (var i = 5; i > 0; i--) {
print(i); // 5, 4, 3, 2, 1
}
// Step by two
for (var i = 0; i < 10; i += 2) {
print(i); // 0, 2, 4, 6, 8
}
// Multiple variables
for (var i = 0, j = 10; i < j; i++, j--) {
print('$i $j');
}
The var i in the initializer is scoped to the loop. After the loop, i is not accessible. Using var is preferred over declaring the variable outside, because it keeps the scope tight.
An infinite for loop is written with an empty condition:
for (;;) {
// runs forever unless break is used
}
This form is rare; the while (true) form is more readable.
b. The for-in loop
The for-in loop iterates over the elements of an Iterable. It is the idiomatic way to traverse a list, set, or map’s values.
var names = ['Alice', 'Bob', 'Carol'];
for (var name in names) {
print(name); // Alice, Bob, Carol
}
Each iteration assigns the next element to the loop variable. The variable is a copy for value types and a reference for objects. Modifying the variable inside the body does not affect the collection.
for (var name in names) {
name = name.toUpperCase(); // does not change the list
}
The for-in loop works with any Iterable, which includes lists, sets, and the results of iterators and generators. It also works with maps through the .keys, .values, and .entries properties.
var scores = {'Alice': 95, 'Bob': 87};
for (var name in scores.keys) {
print(name);
}
for (var score in scores.values) {
print(score);
}
for (var entry in scores.entries) {
print('${entry.key}: ${entry.value}');
}
The for-in loop is preferred over the classic for with an index because it is shorter, does not require bounds checking, and works with any iterable. The main reason to use the classic for instead is when the index is needed.
c. The while loop
The while loop checks a condition before each iteration. The body runs zero or more times.
var count = 0;
while (count < 5) {
print(count); // 0, 1, 2, 3, 4
count++;
}
If the condition is false initially, the body never runs:
var count = 10;
while (count < 5) {
print(count); // never runs
count++;
}
The while loop is appropriate when the number of iterations is not known in advance and depends on a condition that changes during the loop.
var input = '';
while (input != 'quit') {
input = readLine();
process(input);
}
A common mistake is a loop that never terminates because the condition never becomes false. Every while loop must have a path that changes the condition, or it must contain a break.
d. The do-while loop
The do-while loop executes the body first, then checks the condition. The body runs at least once.
var count = 0;
do {
print(count); // 0, 1, 2, 3, 4
count++;
} while (count < 5);
Even if the condition is false initially, the body runs once:
var count = 10;
do {
print(count); // prints 10 once
count++;
} while (count < 5);
The do-while is appropriate when the body must run at least once, such as prompting for input and validating it:
String? input;
do {
print('Enter a number:');
input = readLine();
} while (input == null || int.tryParse(input) == null);
The loop runs, reads input, and repeats until the input is a valid number. The first prompt always appears, which is the intent.
A subtle difference: in a do-while, the variables used in the condition must be declared before the loop, because the condition is evaluated after the body.
e. break and continue
The break statement exits the loop immediately. The continue statement skips the rest of the current iteration and proceeds to the next.
for (var i = 0; i < 10; i++) {
if (i == 5) break;
print(i); // 0, 1, 2, 3, 4
}
for (var i = 0; i < 10; i++) {
if (i % 2 == 0) continue;
print(i); // 1, 3, 5, 7, 9
}
break is used to exit early when a condition is met, such as finding the first match in a list:
var numbers = [3, 7, 2, 9, 4];
var found = -1;
for (var i = 0; i < numbers.length; i++) {
if (numbers[i] == 9) {
found = i;
break;
}
}
print(found); // 3
continue is used to skip unwanted iterations, such as processing only valid entries:
for (var item in items) {
if (item == null) continue;
process(item);
}
Both break and continue apply to the innermost loop that contains them. In nested loops, they affect only the inner loop.
f. Labeled loops
A label is an identifier followed by a colon, placed before a loop. It allows break and continue to target a specific loop, which matters in nested loops.
outer:
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (i == 1 && j == 1) {
break outer; // exits both loops
}
print('$i $j');
}
}
Without the label, break would exit only the inner loop, and the outer loop would continue. The label makes the intent explicit.
A labeled continue skips to the next iteration of the labeled loop:
outer:
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (j == 1) {
continue outer; // skips to next i
}
print('$i $j');
}
}
Labels are useful for searching nested structures, breaking out of a matrix traversal, and any case where the exit condition spans multiple loop levels. They should be used sparingly, because they can make control flow harder to follow.
g. forEach and collection methods
Dart collections provide a forEach method that takes a function and applies it to each element.
var names = ['Alice', 'Bob', 'Carol'];
names.forEach((name) {
print(name);
});
The forEach method is a terminal operation on an iterable. It is equivalent to a for-in loop but does not support break or continue, because it is a function call rather than a loop. Inside a forEach, a return statement exits the closure for that element, but does not exit the iteration. To exit the iteration early, a for-in loop with break is required.
// Cannot break out of forEach
names.forEach((name) {
if (name == 'Bob') return; // returns from the closure, not the loop
print(name);
});
For iteration that needs control flow, for-in is the better choice. forEach is appropriate for simple side effects that apply to every element.
Modern Dart prefers the higher-order methods map, where, reduce, and fold over explicit loops for transformations:
// Map each element
var upper = names.map((name) => name.toUpperCase()).toList();
// Filter elements
var short = names.where((name) => name.length <= 3).toList();
// Aggregate
var total = numbers.reduce((a, b) => a + b);
var sum = numbers.fold<int>(0, (acc, n) => acc + n);
These methods express the intent more directly than a loop and compose with each other. They are covered in their own chapter; the point here is that Dart has idiomatic alternatives to loops for common collection operations.
Complete Example Session
// ============================================
// PART 1: CLASSIC FOR LOOP
// ============================================
void main() {
for (var i = 0; i < 5; i++) {
print(i); // 0, 1, 2, 3, 4
}
}
// ============================================
// PART 2: COUNT DOWN AND STEP
// ============================================
void main() {
for (var i = 5; i > 0; i--) {
print(i); // 5, 4, 3, 2, 1
}
for (var i = 0; i < 10; i += 2) {
print(i); // 0, 2, 4, 6, 8
}
}
// ============================================
// PART 3: FOR-IN OVER A LIST
// ============================================
void main() {
var names = ['Alice', 'Bob', 'Carol'];
for (var name in names) {
print(name);
}
}
// ============================================
// PART 4: FOR-IN OVER MAP ENTRIES
// ============================================
void main() {
var scores = {'Alice': 95, 'Bob': 87};
for (var entry in scores.entries) {
print('${entry.key}: ${entry.value}');
}
}
// ============================================
// PART 5: WHILE LOOP
// ============================================
void main() {
var count = 0;
while (count < 5) {
print(count);
count++;
}
}
// ============================================
// PART 6: DO-WHILE LOOP
// ============================================
void main() {
var count = 0;
do {
print(count); // runs at least once
count++;
} while (count < 5);
}
// ============================================
// PART 7: BREAK AND CONTINUE
// ============================================
void main() {
for (var i = 0; i < 10; i++) {
if (i == 5) break;
print(i); // 0, 1, 2, 3, 4
}
for (var i = 0; i < 10; i++) {
if (i % 2 == 0) continue;
print(i); // 1, 3, 5, 7, 9
}
}
// ============================================
// PART 8: FINDING AN ELEMENT WITH BREAK
// ============================================
void main() {
var numbers = [3, 7, 2, 9, 4];
var found = -1;
for (var i = 0; i < numbers.length; i++) {
if (numbers[i] == 9) {
found = i;
break;
}
}
print(found); // 3
}
// ============================================
// PART 9: LABELED BREAK
// ============================================
void main() {
outer:
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (i == 1 && j == 1) {
break outer;
}
print('$i $j');
}
}
// (0,0), (0,1), (0,2), (1,0)
}
// ============================================
// PART 10: FOR-IN WITH COLLECTION METHODS
// ============================================
void main() {
var numbers = [1, 2, 3, 4, 5];
// Classic iteration
for (var n in numbers) {
print(n);
}
// Functional alternative
var doubled = numbers.map((n) => n * 2).toList();
var evens = numbers.where((n) => n % 2 == 0).toList();
var sum = numbers.fold<int>(0, (acc, n) => acc + n);
print(doubled); // [2, 4, 6, 8, 10]
print(evens); // [2, 4]
print(sum); // 15
}
These ten parts cover the classic for loop, counting down and stepping, for-in over a list, for-in over map entries, the while loop, the do-while loop, break and continue, finding an element with break, labeled break, and the functional alternatives.
Quick Reference
Loop Constructs
| Loop | Checks | Runs Body | Best For |
|---|---|---|---|
for | Before each | 0+ times | Counting, index matters |
for-in | Each element | Once per element | Collections |
while | Before each | 0+ times | Condition-driven |
do-while | After each | 1+ times | At-least-once |
for Loop Syntax
| Part | Example |
|---|---|
| Initializer | var i = 0 |
| Condition | i < 5 |
| Update | i++ |
| Full | for (var i = 0; i < 5; i++) |
for-in Syntax
| Form | Example |
|---|---|
| List | for (var x in list) |
| Set | for (var x in set) |
| Map keys | for (var k in map.keys) |
| Map values | for (var v in map.values) |
| Map entries | for (var e in map.entries) |
Loop Control
| Statement | Effect |
|---|---|
break | Exit the loop |
continue | Skip to next iteration |
break label | Exit labeled loop |
continue label | Continue labeled loop |
Functional Alternatives
| Method | Purpose |
|---|---|
forEach | Apply function to each element |
map | Transform each element |
where | Filter elements |
reduce | Aggregate to a single value |
fold | Aggregate with initial value |
Best Practices
โ Do This:
// for-in for collections
for (var name in names) { print(name); }
// Classic for when index is needed
for (var i = 0; i < names.length; i++) { print('$i: ${names[i]}'); }
// do-while for at-least-once
do { input = readLine(); } while (input == null);
// break to exit early
if (found) break;
// continue to skip
if (item == null) continue;
โ Don’t Do This:
// Index loop when for-in works
for (var i = 0; i < names.length; i++) { print(names[i]); } // โ use for-in
// forEach when break is needed
names.forEach((n) { if (n == 'x') return; }); // โ use for-in with break
// Mutating collection during iteration
for (var n in list) { list.remove(n); } // โ ConcurrentModificationError
// Unlabeled break in nested loops
for (...) { for (...) { break; } } // โ only exits inner
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Infinite loop | Condition never false | Ensure the condition changes |
| Concurrent modification | Modifying list during iteration | Iterate over a copy |
continue in forEach | forEach is not a loop | Use for-in |
break exits only inner | Nested loops | Use a label |
| Off-by-one | <= instead of < | Use < for length |
do-while variable scope | Condition needs the variable | Declare before the loop |
Real-World Examples
1. Count Up
for (var i = 0; i < 10; i++) {
print(i);
}
2. Count Down
for (var i = 10; i > 0; i--) {
print(i);
}
3. Iterate a List
for (var item in items) {
process(item);
}
4. Iterate Map Entries
for (var entry in map.entries) {
print('${entry.key} = ${entry.value}');
}
5. Search with Break
for (var item in items) {
if (item.id == target) {
found = item;
break;
}
}
6. Skip Invalid Items
for (var item in items) {
if (!item.isValid) continue;
process(item);
}
7. Retry Until Success
var attempts = 0;
var success = false;
while (!success && attempts < 3) {
success = tryOperation();
attempts++;
}
8. Validate Input
String? input;
do {
input = readLine();
} while (input == null || input.isEmpty);
9. Nested Loop with Label
search:
for (var row in matrix) {
for (var cell in row) {
if (cell == target) break search;
}
}
10. Functional Alternative
var total = items.fold<int>(0, (sum, item) => sum + item.price);
Visual
Loop Constructs
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ for: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ init โ check โ body โ update โ check โ body โ ... โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Runs 0 or more times. Index available. โ
โ โ
โ for-in: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ element 1 โ body โ element 2 โ body โ ... โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Runs once per element. No index. โ
โ โ
โ while: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ check โ body โ check โ body โ ... โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Runs 0 or more times. โ
โ โ
โ do-while: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ body โ check โ body โ check โ ... โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Runs 1 or more times. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
break and continue
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ for (var i = 0; i < 10; i++) { โ
โ if (i == 5) break; โ exits loop at i = 5 โ
โ if (i % 2 == 0) continue; โ skips even numbers โ
โ print(i); โ
โ } โ
โ โ
โ Output: 1, 3 โ
โ (0 skipped by continue, 1 printed, 2 skipped, 3 printed, โ
โ 4 skipped, 5 breaks) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Labeled Break
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ outer: โ
โ for (var i = 0; i < 3; i++) { โ
โ for (var j = 0; j < 3; j++) { โ
โ if (i == 1 && j == 1) break outer; โ
โ print('$i $j'); โ
โ } โ
โ } โ
โ โ
โ Output: โ
โ 0 0 โ
โ 0 1 โ
โ 0 2 โ
โ 1 0 โ
โ (break outer exits both loops) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
for vs for-in
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CLASSIC for: for-in: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ for (var i = 0; โ โ for (var name โ โ
โ โ i < names.length; โ โ in names) { โ โ
โ โ i++) { โ โ print(name); โ โ
โ โ print('$i: ${names[i]}');โ โ } โ โ
โ โ } โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Index available No index โ
โ Bounds check needed No bounds check โ
โ Use when index matters Use for plain iteration โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
for | Classic loop with initializer, condition, update |
for-in | Iterates over an Iterable |
while | Checks condition before each iteration |
do-while | Checks condition after each iteration |
break | Exit the loop |
continue | Skip to next iteration |
| Label | outer: before a loop, used with break outer |
forEach | Method on iterables; no break or continue |
| Functional alternatives | map, where, reduce, fold |
| Concurrent modification | Throws ConcurrentModificationError |
Key takeaways:
for-inis the idiomatic way to iterate over collections. It works with anyIterable, requires no bounds checking, and eliminates off-by-one errors. Use it unless the index is needed.- The classic
foris for counting and index-based iteration. The initializer, condition, and update give precise control, and the index is available inside the body. whilechecks before the body;do-whilechecks after. Usewhilewhen the body may run zero times, anddo-whilewhen it must run at least once.breakexits,continueskips. Both apply to the innermost loop unless a label is used. Labels allowbreakandcontinueto target an outer loop from within a nested one.forEachcannot be broken out of. It applies a function to each element but has nobreakorcontinue. For iteration that needs control flow, usefor-in.- Modifying a collection during iteration throws. Adding or removing elements while iterating with
for-inorforEachproduces aConcurrentModificationError. Iterate over a copy or collect the changes and apply them after. - Functional methods express transformation more directly.
map,where,reduce, andfoldare often clearer than explicit loops when the goal is to transform or aggregate a collection.
Remember: Dart provides four loop constructs because there are four distinct iteration needs. for-in is the default for collections; the classic for is for counting and when the index matters; while is for condition-driven repetition; do-while guarantees at least one execution. The control statements break and continue handle early exit and skipping, and labels extend them to nested loops. The forEach method is a terminal operation that cannot be controlled with break or continue, and modifying a collection during iteration throws. For transformations and aggregations, the functional methods map, where, reduce, and fold are often more expressive than a loop. Choosing the right construct for the situation makes iteration readable and correct.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!