Flow control is what separates a program from a simple list of instructions. It gives code the ability to make decisions, repeat actions, and skip over blocks based on conditions. Dart's flow control syntax is familiar to anyone coming from Java, C#, or JavaScript.
Conditional Statements
if / else if / else
The if statement evaluates a boolean condition. If the condition is true, the block executes. Optional else if and else branches handle alternative cases.
int score = 75;
if (score >= 90) {
print('Grade: A');
} else if (score >= 80) {
print('Grade: B');
} else if (score >= 70) {
print('Grade: C');
} else {
print('Grade: F');
}
// Output: Grade: C
Conditions can use any expression that evaluates to a bool:
String? username;
if (username == null) {
print('Not logged in');
} else if (username.isEmpty) {
print('Username is blank');
} else {
print('Welcome, $username!');
}
switch / case
The switch statement is useful when comparing a single value against multiple known cases. It is more readable than a long chain of if-else if when many discrete values are possible.
int day = 3;
switch (day) {
case 1:
print('Monday');
break;
case 2:
print('Tuesday');
break;
case 3:
print('Wednesday');
break;
case 6:
case 7:
print('Weekend'); // Fall-through: matches 6 OR 7
break;
default:
print('Unknown day');
}
// Output: Wednesday
Important: Each non-empty case block must end with break, return, throw, or continue. Dart does not allow implicit fall-through between non-empty cases.
String status = 'active';
switch (status) {
case 'active':
print('User is active');
break;
case 'suspended':
print('User is suspended');
break;
case 'deleted':
print('User is deleted');
break;
default:
print('Unknown status: $status');
}
Loops
Dart provides five loop constructs. Choosing the right one improves both readability and intent.
Loop Types
-----------
for --> Known number of iterations
for-in --> Iterate over every element in a collection
forEach --> Functional iteration with a callback
while --> Iterate while a condition is true
do-while --> Iterate at least once, then check condition
Standard for Loop
Use the classic for loop when you need the index or when iterating a specific number of times.
// Counting up
for (int i = 1; i <= 5; i++) {
print(i); // 1, 2, 3, 4, 5
}
// Counting down
for (int i = 5; i > 0; i--) {
print(i); // 5, 4, 3, 2, 1
}
// With step
for (int i = 0; i <= 10; i += 2) {
print(i); // 0, 2, 4, 6, 8, 10
}
for-in Loop
Use for-in when you want every element of a collection but do not need the index. It is more readable than a standard for loop for simple iteration.
List<String> fruits = ['Apple', 'Banana', 'Cherry'];
for (var fruit in fruits) {
print(fruit);
}
// Output: Apple, Banana, Cherry
Set<int> primes = {2, 3, 5, 7, 11};
for (var prime in primes) {
print(prime);
}
forEach
The forEach method on collections accepts a function and calls it once for each element. It is idiomatic Dart for functional-style iteration.
List<String> names = ['Alice', 'Bob', 'Charlie'];
// Using a named function
void printName(String name) {
print(name);
}
names.forEach(printName);
// Using a lambda (anonymous function)
names.forEach((name) => print(name));
// With an arrow function
names.forEach(print); // Most concise form
while Loop
The while loop checks its condition before each iteration. If the condition starts as false, the body never executes.
int countdown = 5;
while (countdown > 0) {
print(countdown);
countdown--;
}
print('Blast off!');
// Output: 5, 4, 3, 2, 1, Blast off!
Use while when the number of iterations is not known in advance and the condition might be false from the start.
do-while Loop
The do-while loop executes the body once before checking the condition. This guarantees at least one iteration.
int num = 0;
do {
print(num);
num++;
} while (num < 5);
// Output: 0, 1, 2, 3, 4
A common use case is prompting the user until they provide valid input:
String? input;
do {
input = getUserInput(); // Always ask at least once
} while (input == null || input.isEmpty);
Loop Control: break and continue
break
Immediately exits the loop, skipping all remaining iterations.
List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for (var n in numbers) {
if (n == 6) break; // Stop when we reach 6
print(n);
}
// Output: 1, 2, 3, 4, 5
continue
Skips the current iteration and moves to the next one, without exiting the loop.
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) continue; // Skip even numbers
print(i);
}
// Output: 1, 3, 5, 7, 9
Choosing the Right Construct
Decision Guide
--------------
Single condition?
--> if/else
Multiple discrete values?
--> switch/case
Known number of iterations?
--> for
Iterating a collection (no index needed)?
--> for-in or forEach
Unknown iterations, condition-first?
--> while
Must execute at least once?
--> do-while
Using the most appropriate construct makes code intent immediately clear to the reader — and to your future self.