Control Flow: if, switch & Loops
So far every program has run from top to bottom, one line after another. Real programs make decisions ("is the ticket waitlisted?") and repeat work ("print a row for every student"). The statements that do this are called control flow.
In this lesson you will learn if and else, Dart 3's modern switch statements and switch expressions, and every kind of loop. These are the same tools you will later use to decide which widget to show on a Flutter screen.
if, else if and else
if runs a block only when a condition is true. Add else if for more conditions and else for "everything else". Dart checks them from top to bottom and runs only the first matching block.
void main() {
const percentage = 76;
if (percentage >= 90) {
print('Grade A+');
} else if (percentage >= 75) {
print('Grade A');
} else if (percentage >= 60) {
print('Grade B');
} else {
print('Keep practising');
}
const isRaining = true;
if (isRaining) print('Take an umbrella to college.');
}Grade A Take an umbrella to college.
The condition must be a real bool. Unlike some languages, Dart does not treat 0, an empty string or null as false, so if (count) is a compile error; write if (count > 0). Braces are optional for a single statement, but always using them avoids mistakes when you later add a second line.
switch statements
When you compare one value against many possibilities, switch is clearer than a long if chain. In Dart 3, each case ends automatically; you do not need break. A case can also match several values with ||.
void main() {
const day = 'Sunday';
switch (day) {
case 'Saturday' || 'Sunday':
print('Weekend: the shop opens at 11 am');
case 'Friday':
print('Friday: open till 10 pm');
default:
print('Weekday: open 9 am to 9 pm');
}
const coach = 'S4';
switch (coach) {
case 'A1' || 'B1':
print('AC coach');
case _ when coach.startsWith('S'):
print('Sleeper coach $coach');
case _:
print('General coach');
}
}Weekend: the shop opens at 11 am Sleeper coach S4
default and case _ both mean "anything else". _ is the wildcard: it matches any value. A when clause adds an extra condition to a case, called a guard. These are part of Dart's patterns, which you will study properly in Records & Patterns.
switch expressions
Often you only want to turn one value into another value. A switch expression does this in a compact way, and it returns a result you can store:
String grade(int marks) => switch (marks) {
>= 90 => 'A+',
>= 75 => 'A',
>= 60 => 'B',
>= 40 => 'C',
_ => 'Fail',
};
void main() {
for (final marks in [95, 81, 40, 12]) {
print('$marks marks: ${grade(marks)}');
}
const signal = 'amber';
final action = switch (signal) {
'red' => 'Stop',
'amber' => 'Get ready',
'green' => 'Go',
_ => 'Signal not working, drive carefully',
};
print(action);
}95 marks: A+ 81 marks: A 40 marks: C 12 marks: Fail Get ready
In a switch expression, each case is pattern => value, separated by commas, with no case keyword. >= 90 is a relational pattern: it matches any number greater than or equal to 90. The compiler checks that a switch expression covers every possible value (it is exhaustive), which is why the _ case is needed for strings and numbers.
for and for-in loops
A classic for loop has three parts: a starting value, a condition, and a step. A for-in loop goes through every item of a collection.
void main() {
for (var i = 1; i <= 5; i++) {
print('7 x $i = ${7 * i}');
}
final cities = ['Rohtak', 'Jaipur', 'Kochi'];
for (final city in cities) {
print('Delivering to $city');
}
var total = 0;
for (final price in [120, 80, 250]) {
total += price;
}
print('Cart total: ₹$total');
}7 x 1 = 7 7 x 2 = 14 7 x 3 = 21 7 x 4 = 28 7 x 5 = 35 Delivering to Rohtak Delivering to Jaipur Delivering to Kochi Cart total: ₹450
Use for-in whenever you just need each item; it is shorter and you cannot make an off-by-one mistake with the index. Use the classic for when you need the index or a custom step, such as i += 2.
while and do-while loops
A while loop repeats as long as a condition stays true. Use it when you do not know in advance how many times the loop will run.
void main() {
var savings = 0;
var month = 0;
const goal = 5000;
while (savings < goal) {
month++;
savings += 1200;
}
print('Goal reached in $month months with ₹$savings');
var attempts = 0;
do {
attempts++;
print('OTP attempt $attempts');
} while (attempts < 3 && attempts != 2);
}Goal reached in 5 months with ₹6000 OTP attempt 1 OTP attempt 2
A do-while loop checks the condition after the body, so the body always runs at least once. Be careful with while: if the condition never becomes false, the loop runs forever and your program hangs.
break and continue
break leaves a loop immediately. continue skips the rest of the current round and moves to the next one.
void main() {
final seats = ['booked', 'booked', 'free', 'booked', 'free'];
for (var i = 0; i < seats.length; i++) {
if (seats[i] == 'booked') continue;
print('First free seat is number ${i + 1}');
break;
}
for (var n = 1; n <= 10; n++) {
if (n.isEven) continue;
print(n);
}
}First free seat is number 3 1 3 5 7 9
The first loop skips booked seats with continue, then stops at the first free one with break. Without break, it would also report seat 5.
Common mistakes
- Using a non-bool condition.
if (items.length)does not compile. Writeif (items.isNotEmpty). - Forgetting the
_case in a switch expression over strings or numbers. The compiler reports that the switch is not exhaustive. - Infinite
whileloops. Make sure something inside the loop moves the condition towardsfalse. - Changing a list while looping over it with for-in. This throws an error at run time. Build a new list instead (you will see how in the next lessons).
Key points
if,else ifandelsechoose one block; conditions must bebool.- Dart 3
switchcases do not fall through and need nobreak;||matches several values. - Switch expressions (
pattern => value) return a value and must be exhaustive. - Relational patterns like
>= 90, the wildcard_andwhenguards make cases expressive. - Use for-in for collections, classic
forfor indexes,whilewhen the count is unknown. breakexits a loop;continueskips to the next round.
Exercise
An electricity board charges ₹3 per unit for the first 100 units, ₹5 per unit for units 101 to 200, and ₹7 per unit above 200. Write a function int bill(int units) and print the bill for 80, 150 and 260 units using a for-in loop. Then write a switch expression that turns a number of units into the label 'Low' (below 100), 'Medium' (100 to 199) or 'High' (200 or more).
Show solution
int bill(int units) {
if (units <= 100) {
return units * 3;
} else if (units <= 200) {
return 100 * 3 + (units - 100) * 5;
} else {
return 100 * 3 + 100 * 5 + (units - 200) * 7;
}
}
String usage(int units) => switch (units) {
< 100 => 'Low',
< 200 => 'Medium',
_ => 'High',
};
void main() {
for (final units in [80, 150, 260]) {
print('$units units: ₹${bill(units)} (${usage(units)})');
}
}80 units: ₹240 (Low) 150 units: ₹550 (Medium) 260 units: ₹1220 (High)
Each slab adds the full cost of the earlier slabs, and the switch expression checks the ranges from the smallest upwards, so the first matching pattern wins.
Quick Quiz
1. In a Dart 3 switch statement, what happens at the end of a non-empty case?
Dart 3 cases end automatically; old code may still use break, which is allowed.
2. Which loop always runs its body at least once?
do-while checks the condition after the body.
3. What does case _ when coach.startsWith('S'): do?
_ matches anything and when adds an extra condition.
4. Why does if (count) fail when count is an int?
Dart never converts numbers to true or false automatically.
Learn Flutter & Dart with a mentor
Free lessons get you started. Our Mobile App Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
