On this page24 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. What Is a Switch Expression?
- 2. Your First Switch Expression
- 3. The Parts of a Switch Expression
- 4. The Wildcard _
- 5. Exhaustiveness: Every Value Must Be Covered
- 6. When _ Is Not Needed
- 7. Several Values in One Arm with ||
- 8. Relational Patterns: Matching Ranges
- 9. Exact Ranges with &&
- 10. Extra Conditions with when
- 11. Switch Expression, Switch Statement, or if?
- 12. A Real-World Example
- Code Examples
- Common Beginner Mistakes
- Best Practices
- Mini Challenge
- Practice Questions
- Practice Answers
- Quick Revision
- Key Takeaways
- What's Next?
Lecture 25 — Modern Switch Expressions
| Category | 5 — Control Flow |
| Lecture | 025 of 140 |
| Estimated video length | 10 min |
| Target | Dart 3.12.2 |
| Level | Beginner |
A
switchstatement does something in each case. Very often all you want is to pick a value. A switch expression gives you that value directly — in one short, checked block.
Contents
Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways
Every error message in this lecture is real. Each one was produced by running the code shown on Dart 3.12.2, so you will recognize it when you meet it yourself.
Prerequisites
Students should already understand, from Lectures 004–024:
var, the basic types, and string interpolation- nullable types such as
bool?andint?, andint.tryParse(Lectures 010–013) - comparison operators (Lecture 018) and
&&,||(Lecture 019) - the conditional operator
? :(Lecture 020) if,else if, andelse(Lectures 022–023)switchstatements withcaseanddefault(Lecture 024)
Connection With Previous Lectures
In Lecture 024 we used a switch statement to turn a day number into a name:
void main() {
var day = 3;
String dayName;
switch (day) {
case 1:
dayName = 'Monday';
case 2:
dayName = 'Tuesday';
case 3:
dayName = 'Wednesday';
default:
dayName = 'Unknown';
}
print(dayName);
}WednesdayIt works, but dayName starts with no value and dayName = is repeated in every case — when
all this switch really does is choose one value. And for ranges like "90 or more is an A"
we still needed an else if chain. Dart 3 has a shorter tool for both.
Learning Objectives
By the end of this lecture you will be able to:
- Write a switch expression with
=>arms, and tell it apart from a switch statement - Use the wildcard
_to catch every other value - Explain what exhaustiveness means and read the error when a value is not covered
- Match several values in one arm with
|| - Match ranges with relational patterns such as
>= 90 - Add an extra condition to an arm with
when - Choose between a switch expression, a switch statement, and
if
1. What Is a Switch Expression?
A switch expression looks at a value, finds the first matching arm, and produces that
arm's result. Like ? : from Lecture 020, it is a value, so it can go into a variable or
straight into print().
Analogy — think of a price list on a café wall. You find your item's row and read the price. You don't do anything in each row; each row simply gives an answer.
2. Your First Switch Expression
Here is the day-name program again:
void main() {
var day = 3;
var dayName = switch (day) {
1 => 'Monday',
2 => 'Tuesday',
3 => 'Wednesday',
_ => 'Unknown',
};
print(dayName);
}Wednesdayday is 3, so the third arm matches. One assignment, no repetition — and var works
because every arm gives a String.
3. The Parts of a Switch Expression
| Part | Meaning |
|---|---|
switch (day) | the value being checked |
1 => 'Monday' | an arm: a pattern, =>, and the result |
, | separates arms (also after the last arm) |
_ => 'Unknown' | the wildcard arm: matches anything |
}; | the closing brace, plus ; to end the whole statement |
No case, no :, no default — and each arm holds one expression, not statements.
4. The Wildcard _
_ matches any value. It plays the role default played in Lecture 024:
void main() {
var light = 'blue';
var action = switch (light) {
'green' => 'Go',
'yellow' => 'Slow down',
'red' => 'Stop',
_ => 'Invalid light',
};
print(action);
}Invalid lightDart tries the arms from top to bottom and stops at the first match, so _ always goes
last.
5. Exhaustiveness: Every Value Must Be Covered
A switch expression must produce a value every time. So Dart checks that your arms cover
every possible value — this is called exhaustiveness. Leave out _ on an int, and the
program does not compile:
void main() {
var day = 3;
var dayName = switch (day) {
1 => 'Monday',
2 => 'Tuesday',
3 => 'Wednesday',
};
print(dayName);
}Error: The type 'int' is not exhaustively matched by the switch cases since it doesn't match 'int()'.
Try adding a wildcard pattern or cases that match 'int()'.
var dayName = switch (day) {
^In plain words: "some int values match none of your arms." Add _ => 'Unknown'.
6. When _ Is Not Needed
Some types have only a few possible values. A bool is either true or false, so two arms
cover everything:
void main() {
var isOnline = true;
var status = switch (isOnline) {
true => 'Online',
false => 'Offline',
};
print(status);
bool? answer;
var reply = switch (answer) {
true => 'Yes',
false => 'No',
null => 'No answer',
};
print(reply);
}Online
No answerA bool? can also be null, so it needs a third arm. Forget one and you get the error from
section 5.
7. Several Values in One Arm with ||
When several values share one result, join them with || inside the pattern:
void main() {
var day = 'Sunday';
var kind = switch (day) {
'Saturday' || 'Sunday' => 'Weekend',
_ => 'Weekday',
};
print(kind);
}WeekendRead it as "Saturday or Sunday". Here || joins two patterns, not two bool values as
in Lecture 019 — but the meaning is the same.
8. Relational Patterns: Matching Ranges
A pattern can also be a comparison operator followed by a value — a relational pattern:
void main() {
var marks = 82;
var grade = switch (marks) {
>= 90 => 'A',
>= 80 => 'B',
>= 70 => 'C',
>= 50 => 'D',
_ => 'F',
};
print(grade);
}B>= 90 means "the value is 90 or more". 82 fails the first arm and matches the second.
Order matters: 82 is also >= 70 and >= 50, but Dart stops at the first match. You
can use <, <=, >, >=, ==, and != this way.
9. Exact Ranges with &&
To match a range with both ends, join two relational patterns with &&:
void main() {
var age = 15;
var price = switch (age) {
< 5 => 0,
>= 5 && < 18 => 300,
>= 60 => 400,
_ => 800,
};
print('Ticket: $price');
}Ticket: 300>= 5 && < 18 means "at least 5 and less than 18" — the same range you wrote as
age >= 5 && age < 18 in Lecture 019, without repeating age.
10. Extra Conditions with when
Sometimes the choice depends on another variable too. Add a guard with when: the arm
matches only if its pattern matches and the condition is true:
void main() {
var city = 'Karachi';
var total = 6000;
var shipping = switch (city) {
'Karachi' when total >= 5000 => 0,
'Karachi' => 200,
'Lahore' || 'Islamabad' => 350,
_ => 600,
};
print('Shipping: $shipping');
}Shipping: 0With total = 3000 the guard is false, so Dart moves on and the second arm gives 200.
11. Switch Expression, Switch Statement, or if?
- Switch expression — you are choosing one value from one variable: a name, a grade, a price.
- Switch statement — each case must do several things. The patterns from this
lecture work after
casetoo. if/else— the conditions involve unrelated variables.
We will study patterns properly in Category 13 — Patterns & Modern Dart (Lectures 077–081), and see exhaustiveness with enums in Lecture 067 and with sealed classes in Lecture 082.
12. A Real-World Example
void main() {
var student = 'Sara';
var marks = 91;
var hasProject = true;
var grade = switch (marks) {
>= 90 => 'A',
>= 75 => 'B',
>= 50 => 'C',
_ => 'F',
};
var award = switch (grade) {
'A' when hasProject => 'Gold medal',
'A' || 'B' => 'Certificate',
_ => 'Keep practising',
};
print('Student: $student');
print('Grade: $grade');
print('Award: $award');
}Student: Sara
Grade: A
Award: Gold medalRelational patterns turn marks into a grade; a when guard and an || pattern pick the award.
Code Examples
Example 1 — Month Names
void main() {
var month = 2;
var name = switch (month) {
1 => 'January',
2 => 'February',
3 => 'March',
_ => 'Later in the year',
};
print(name);
}FebruaryExample 2 — Matching Strings
void main() {
var size = 'M';
var label = switch (size) {
'S' => 'Small',
'M' => 'Medium',
'L' => 'Large',
_ => 'Unknown size',
};
print(label);
}MediumExample 3 — A bool Without _
void main() {
var isRaining = false;
var advice = switch (isRaining) {
true => 'Take an umbrella',
false => 'Enjoy the sun',
};
print(advice);
}Enjoy the sunExample 4 — Grouping with ||
void main() {
var month = 7;
var season = switch (month) {
12 || 1 || 2 => 'Winter',
3 || 4 || 5 => 'Spring',
6 || 7 || 8 => 'Summer',
_ => 'Autumn',
};
print(season);
}SummerExample 5 — Relational Patterns
void main() {
var speed = 130;
var message = switch (speed) {
> 120 => 'Too fast',
>= 60 => 'Normal',
_ => 'Slow',
};
print(message);
}Too fastExample 6 — Calculating Inside an Arm
void main() {
var hours = 12;
var rate = 500;
var pay = switch (hours) {
<= 8 => hours * rate,
_ => 8 * rate + (hours - 8) * rate * 2,
};
print('Pay: $pay');
}Pay: 8000An arm's result can be a calculation. Hours beyond 8 are paid double.
Example 7 — Student Marks
void main() {
var student = 'Ali';
var marks = 67;
var result = switch (marks) {
>= 80 => 'Distinction',
>= 50 && < 80 => 'Pass',
_ => 'Fail',
};
print('$student: $marks — $result');
}Ali: 67 — PassExample 8 — Shopping Total
void main() {
var total = 4500;
var discount = switch (total) {
>= 10000 => 0.2,
>= 3000 => 0.1,
_ => 0.0,
};
var finalPrice = total - total * discount;
print('Discount: ${discount * 100}%');
print('Final price: $finalPrice');
}Discount: 10.0%
Final price: 4050.0Every arm gives a double, so discount and finalPrice are double values.
Example 9 — Bank Balance
void main() {
var balance = 800.0;
var isPremium = true;
var fee = switch (balance) {
< 0 => 500,
< 1000 when isPremium => 0,
< 1000 => 100,
_ => 0,
};
print('Monthly fee: $fee');
}Monthly fee: 0A regular customer with the same balance pays 100.
Example 10 — Temperature
void main() {
var celsius = 34.5;
var feeling = switch (celsius) {
< 0 => 'Freezing',
< 15 => 'Cold',
< 25 => 'Pleasant',
< 35 => 'Warm',
_ => 'Very hot',
};
print('$celsius°C feels $feeling');
}34.5°C feels WarmRelational patterns work on double values too.
Common Beginner Mistakes
1. Forgetting _
void main() {
var day = 3;
// var name = switch (day) { 1 => 'Monday', 2 => 'Tuesday' }; // ✗
}Error: The type 'int' is not exhaustively matched by the switch cases since it doesn't match 'int()'.Add a final _ arm.
2. Writing case or default
void main() {
var day = 3;
// var name = switch (day) { case 1 => 'Monday', _ => 'Other' }; // ✗
// var name = switch (day) { 1 => 'Monday', default => 'Other' }; // ✗
}Error: Unexpected token 'case'.
Error: A switch expression may not use the `default` keyword.
Try replacing `default` with `_`.Those keywords belong to the switch statement.
3. Using : or ; inside the arms
void main() {
var day = 3;
// var name = switch (day) { 1: 'Monday', _: 'Other' }; // ✗
// var name = switch (day) { 1 => 'Monday'; _ => 'Other'; }; // ✗
}Error: Expected '=>' before this.
Error: Expected ',' before this.Each arm uses =>, and arms are separated by commas.
4. Forgetting the final ;
void main() {
var day = 3;
// var name = switch (day) { 1 => 'Monday', _ => 'Other' } // ✗
}Error: Expected ';' after this.The whole line is an assignment, so it ends with };.
5. Putting the widest range first
void main() {
var marks = 95;
var grade = switch (marks) {
>= 50 => 'Pass',
>= 90 => 'Excellent', // never reached
_ => 'Fail',
};
print(grade);
}PassNo error — just a wrong answer: 95 matches >= 50 first. Put the strictest arm first.
Best Practices
- Use a switch expression when you are choosing a value; use a statement when each case does several things.
- Keep
_last; for abool, listingtrueandfalseis clearer than_. - Order relational patterns from strictest to widest (
>= 90before>= 80). - Group values that share a result with
||instead of repeating the result.
Mini Challenge
Zayan's science kit has a thermometer. Write a program that stores temperature = 105 and
uses a switch expression to print the state of water: below 0 is Ice, below 100 is
Water, anything else is Steam.
Your program should print exactly:
At 105°C water is SteamTry it yourself before opening the solution.
Show solution
void main() {
var temperature = 105;
var state = switch (temperature) {
< 0 => 'Ice',
< 100 => 'Water',
_ => 'Steam',
};
print('At $temperature°C water is $state');
}At 105°C water is SteamPractice Questions
Work through these in order — they run from beginner to application to challenge. Every question gives you exact values, so you can compare your output with the answers.
Beginner
-
Store
level = 2and use a switch expression to printEasyfor 1,Mediumfor 2,Hardfor 3, andUnknownfor anything else. -
Store
hasTicket = true. Using a switch expression without_, printEnterorBuy a ticket first. -
This program does not compile. Fix it so it prints
Other:void main() { var code = 9; var name = switch (code) { 1 => 'One', default => 'Other', }; print(name); }
Application
-
Store
letter = 'e'. PrintVowelif it is'a','e','i','o', or'u', andConsonantotherwise. Use one||pattern. -
Store
battery = 15. PrintFullfor 80 or more,Goodfor 30 or more, andLowotherwise. -
Store
age = 65and printPrice: 400using the ticket rules from section 9. -
Rewrite this
ifchain as a switch expression with the same output:void main() { var celsius = 8; String feeling; if (celsius < 10) { feeling = 'Cold'; } else if (celsius < 25) { feeling = 'Mild'; } else { feeling = 'Hot'; } print(feeling); }
Challenge
-
Predict what this prints, then reorder the arms so it prints
Gold:void main() { var points = 1200; var tier = switch (points) { >= 100 => 'Bronze', >= 500 => 'Silver', >= 1000 => 'Gold', _ => 'None', }; print(tier); } -
Store
city = 'Lahore'andtotal = 7000. Shipping is free in any city when the total is5000or more; otherwise it is200in Karachi and350everywhere else. Use onewhenguard and printShipping: 0. -
Store
int? score = int.tryParse('abc');. PrintInvalidwhenscoreisnull,Passfor 50 or more, andFailotherwise. Explain why thenullarm is needed.
Practice Answers
Question 1 — Solution
void main() {
var level = 2;
var name = switch (level) {
1 => 'Easy',
2 => 'Medium',
3 => 'Hard',
_ => 'Unknown',
};
print(name);
}MediumQuestion 2 — Solution
void main() {
var hasTicket = true;
var message = switch (hasTicket) {
true => 'Enter',
false => 'Buy a ticket first',
};
print(message);
}EnterQuestion 3 — Solution
Replace default with _:
void main() {
var code = 9;
var name = switch (code) {
1 => 'One',
_ => 'Other',
};
print(name);
}OtherQuestion 4 — Solution
void main() {
var letter = 'e';
var kind = switch (letter) {
'a' || 'e' || 'i' || 'o' || 'u' => 'Vowel',
_ => 'Consonant',
};
print(kind);
}VowelQuestion 5 — Solution
void main() {
var battery = 15;
var status = switch (battery) {
>= 80 => 'Full',
>= 30 => 'Good',
_ => 'Low',
};
print(status);
}LowQuestion 6 — Solution
void main() {
var age = 65;
var price = switch (age) {
< 5 => 0,
>= 5 && < 18 => 300,
>= 60 => 400,
_ => 800,
};
print('Price: $price');
}Price: 400Question 7 — Solution
void main() {
var celsius = 8;
var feeling = switch (celsius) {
< 10 => 'Cold',
< 25 => 'Mild',
_ => 'Hot',
};
print(feeling);
}ColdQuestion 8 — Solution
As written it prints Bronze, because 1200 matches >= 100 first. Put the strictest arm
first:
void main() {
var points = 1200;
var tier = switch (points) {
>= 1000 => 'Gold',
>= 500 => 'Silver',
>= 100 => 'Bronze',
_ => 'None',
};
print(tier);
}GoldQuestion 9 — Solution
The guard goes on a wildcard arm, so it applies to every city:
void main() {
var city = 'Lahore';
var total = 7000;
var shipping = switch (city) {
_ when total >= 5000 => 0,
'Karachi' => 200,
_ => 350,
};
print('Shipping: $shipping');
}Shipping: 0_ when ... matches only when the guard is true, so the arms below it are still reached.
Question 10 — Solution
void main() {
int? score = int.tryParse('abc');
var result = switch (score) {
null => 'Invalid',
>= 50 => 'Pass',
_ => 'Fail',
};
print(result);
}Invalid'abc' is not a number, so score is null. >= 50 never matches null, so without its
own arm, null would land in _ and print Fail.
Quick Revision
- What is the main difference between a switch statement and a switch expression?
- What symbol separates a pattern from its result? What separates two arms?
- What does
_match, and where should it go? - What does "exhaustive" mean?
- Why does a
switchon aboolnot need_? - In what order does Dart try relational patterns such as
>= 90and>= 80? - What does
whenadd to an arm?
Key Takeaways
- A switch expression chooses and produces a value:
switch (x) { a => ..., _ => ... }. - Arms are
pattern => result, separated by commas; nocase, nodefault, end with};. - Dart tries arms from top to bottom and uses the first match.
_matches anything; it replacesdefaultand goes last.- A switch expression must be exhaustive — every possible value covered — or it does not
compile. A
boolneeds onlytrueandfalse. ||puts several values in one arm; relational patterns such as>= 90match ranges, and&&joins two of them.- A
whenguard adds an extra condition that must also betrue.
What's Next?
Lecture 026 — for Loops. Your program can now choose which code runs, but every line
still runs at most once. Counting from 1 to 100 would mean writing the same line a hundred
times. Next, a for loop repeats code as many times as you need.