Watch the video (10 min)
On this page24 sections
  1. Prerequisites
  2. Connection With Previous Lectures
  3. Learning Objectives
  4. 1. What Is a Switch Expression?
  5. 2. Your First Switch Expression
  6. 3. The Parts of a Switch Expression
  7. 4. The Wildcard _
  8. 5. Exhaustiveness: Every Value Must Be Covered
  9. 6. When _ Is Not Needed
  10. 7. Several Values in One Arm with ||
  11. 8. Relational Patterns: Matching Ranges
  12. 9. Exact Ranges with &&
  13. 10. Extra Conditions with when
  14. 11. Switch Expression, Switch Statement, or if?
  15. 12. A Real-World Example
  16. Code Examples
  17. Common Beginner Mistakes
  18. Best Practices
  19. Mini Challenge
  20. Practice Questions
  21. Practice Answers
  22. Quick Revision
  23. Key Takeaways
  24. What's Next?

Lecture 25 — Modern Switch Expressions

Category5 — Control Flow
Lecture025 of 140
Estimated video length10 min
TargetDart 3.12.2
LevelBeginner

A switch statement 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? and int?, and int.tryParse (Lectures 010–013)
  • comparison operators (Lecture 018) and &&, || (Lecture 019)
  • the conditional operator ? : (Lecture 020)
  • if, else if, and else (Lectures 022–023)
  • switch statements with case and default (Lecture 024)

Connection With Previous Lectures

In Lecture 024 we used a switch statement to turn a day number into a name:

dart
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);
}
output
Wednesday

It 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:

dart
void main() {
  var day = 3;
  var dayName = switch (day) {
    1 => 'Monday',
    2 => 'Tuesday',
    3 => 'Wednesday',
    _ => 'Unknown',
  };
  print(dayName);
}
output
Wednesday

day 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

PartMeaning
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:

dart
void main() {
  var light = 'blue';
  var action = switch (light) {
    'green' => 'Go',
    'yellow' => 'Slow down',
    'red' => 'Stop',
    _ => 'Invalid light',
  };
  print(action);
}
output
Invalid light

Dart 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:

dart
void main() {
  var day = 3;
  var dayName = switch (day) {
    1 => 'Monday',
    2 => 'Tuesday',
    3 => 'Wednesday',
  };
  print(dayName);
}
output
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:

dart
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);
}
output
Online
No answer

A 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:

dart
void main() {
  var day = 'Sunday';
  var kind = switch (day) {
    'Saturday' || 'Sunday' => 'Weekend',
    _ => 'Weekday',
  };
  print(kind);
}
output
Weekend

Read 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:

dart
void main() {
  var marks = 82;
  var grade = switch (marks) {
    >= 90 => 'A',
    >= 80 => 'B',
    >= 70 => 'C',
    >= 50 => 'D',
    _ => 'F',
  };
  print(grade);
}
output
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 &&:

dart
void main() {
  var age = 15;
  var price = switch (age) {
    < 5 => 0,
    >= 5 && < 18 => 300,
    >= 60 => 400,
    _ => 800,
  };
  print('Ticket: $price');
}
output
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:

dart
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');
}
output
Shipping: 0

With 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 case too.
  • 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

dart
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');
}
output
Student: Sara
Grade: A
Award: Gold medal

Relational patterns turn marks into a grade; a when guard and an || pattern pick the award.


Code Examples

Example 1 — Month Names

dart
void main() {
  var month = 2;
  var name = switch (month) {
    1 => 'January',
    2 => 'February',
    3 => 'March',
    _ => 'Later in the year',
  };
  print(name);
}
output
February

Example 2 — Matching Strings

dart
void main() {
  var size = 'M';
  var label = switch (size) {
    'S' => 'Small',
    'M' => 'Medium',
    'L' => 'Large',
    _ => 'Unknown size',
  };
  print(label);
}
output
Medium

Example 3 — A bool Without _

dart
void main() {
  var isRaining = false;
  var advice = switch (isRaining) {
    true => 'Take an umbrella',
    false => 'Enjoy the sun',
  };
  print(advice);
}
output
Enjoy the sun

Example 4 — Grouping with ||

dart
void main() {
  var month = 7;
  var season = switch (month) {
    12 || 1 || 2 => 'Winter',
    3 || 4 || 5 => 'Spring',
    6 || 7 || 8 => 'Summer',
    _ => 'Autumn',
  };
  print(season);
}
output
Summer

Example 5 — Relational Patterns

dart
void main() {
  var speed = 130;
  var message = switch (speed) {
    > 120 => 'Too fast',
    >= 60 => 'Normal',
    _ => 'Slow',
  };
  print(message);
}
output
Too fast

Example 6 — Calculating Inside an Arm

dart
void main() {
  var hours = 12;
  var rate = 500;
  var pay = switch (hours) {
    <= 8 => hours * rate,
    _ => 8 * rate + (hours - 8) * rate * 2,
  };
  print('Pay: $pay');
}
output
Pay: 8000

An arm's result can be a calculation. Hours beyond 8 are paid double.

Example 7 — Student Marks

dart
void main() {
  var student = 'Ali';
  var marks = 67;
  var result = switch (marks) {
    >= 80 => 'Distinction',
    >= 50 && < 80 => 'Pass',
    _ => 'Fail',
  };
  print('$student: $marks — $result');
}
output
Ali: 67 — Pass

Example 8 — Shopping Total

dart
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');
}
output
Discount: 10.0%
Final price: 4050.0

Every arm gives a double, so discount and finalPrice are double values.

Example 9 — Bank Balance

dart
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');
}
output
Monthly fee: 0

A regular customer with the same balance pays 100.

Example 10 — Temperature

dart
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');
}
output
34.5°C feels Warm

Relational patterns work on double values too.


Common Beginner Mistakes

1. Forgetting _

dart
void main() {
  var day = 3;
  // var name = switch (day) { 1 => 'Monday', 2 => 'Tuesday' }; // ✗
}
output
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

dart
void main() {
  var day = 3;
  // var name = switch (day) { case 1 => 'Monday', _ => 'Other' }; // ✗
  // var name = switch (day) { 1 => 'Monday', default => 'Other' }; // ✗
}
output
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

dart
void main() {
  var day = 3;
  // var name = switch (day) { 1: 'Monday', _: 'Other' }; // ✗
  // var name = switch (day) { 1 => 'Monday'; _ => 'Other'; }; // ✗
}
output
Error: Expected '=>' before this.
Error: Expected ',' before this.

Each arm uses =>, and arms are separated by commas.

4. Forgetting the final ;

dart
void main() {
  var day = 3;
  // var name = switch (day) { 1 => 'Monday', _ => 'Other' } // ✗
}
output
Error: Expected ';' after this.

The whole line is an assignment, so it ends with };.

5. Putting the widest range first

dart
void main() {
  var marks = 95;
  var grade = switch (marks) {
    >= 50 => 'Pass',
    >= 90 => 'Excellent', // never reached
    _ => 'Fail',
  };
  print(grade);
}
output
Pass

No 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 a bool, listing true and false is clearer than _.
  • Order relational patterns from strictest to widest (>= 90 before >= 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:

output
At 105°C water is Steam

Try it yourself before opening the solution.

Show solution
dart
void main() {
  var temperature = 105;
  var state = switch (temperature) {
    < 0 => 'Ice',
    < 100 => 'Water',
    _ => 'Steam',
  };
  print('At $temperature°C water is $state');
}
output
At 105°C water is Steam

Practice 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

  1. Store level = 2 and use a switch expression to print Easy for 1, Medium for 2, Hard for 3, and Unknown for anything else.

  2. Store hasTicket = true. Using a switch expression without _, print Enter or Buy a ticket first.

  3. This program does not compile. Fix it so it prints Other:

    dart
    void main() {
      var code = 9;
      var name = switch (code) {
        1 => 'One',
        default => 'Other',
      };
      print(name);
    }

Application

  1. Store letter = 'e'. Print Vowel if it is 'a', 'e', 'i', 'o', or 'u', and Consonant otherwise. Use one || pattern.

  2. Store battery = 15. Print Full for 80 or more, Good for 30 or more, and Low otherwise.

  3. Store age = 65 and print Price: 400 using the ticket rules from section 9.

  4. Rewrite this if chain as a switch expression with the same output:

    dart
    void main() {
      var celsius = 8;
      String feeling;
      if (celsius < 10) {
        feeling = 'Cold';
      } else if (celsius < 25) {
        feeling = 'Mild';
      } else {
        feeling = 'Hot';
      }
      print(feeling);
    }

Challenge

  1. Predict what this prints, then reorder the arms so it prints Gold:

    dart
    void main() {
      var points = 1200;
      var tier = switch (points) {
        >= 100 => 'Bronze',
        >= 500 => 'Silver',
        >= 1000 => 'Gold',
        _ => 'None',
      };
      print(tier);
    }
  2. Store city = 'Lahore' and total = 7000. Shipping is free in any city when the total is 5000 or more; otherwise it is 200 in Karachi and 350 everywhere else. Use one when guard and print Shipping: 0.

  3. Store int? score = int.tryParse('abc');. Print Invalid when score is null, Pass for 50 or more, and Fail otherwise. Explain why the null arm is needed.


Practice Answers

Question 1 — Solution

dart
void main() {
  var level = 2;
  var name = switch (level) {
    1 => 'Easy',
    2 => 'Medium',
    3 => 'Hard',
    _ => 'Unknown',
  };
  print(name);
}
output
Medium

Question 2 — Solution

dart
void main() {
  var hasTicket = true;
  var message = switch (hasTicket) {
    true => 'Enter',
    false => 'Buy a ticket first',
  };
  print(message);
}
output
Enter

Question 3 — Solution

Replace default with _:

dart
void main() {
  var code = 9;
  var name = switch (code) {
    1 => 'One',
    _ => 'Other',
  };
  print(name);
}
output
Other

Question 4 — Solution

dart
void main() {
  var letter = 'e';
  var kind = switch (letter) {
    'a' || 'e' || 'i' || 'o' || 'u' => 'Vowel',
    _ => 'Consonant',
  };
  print(kind);
}
output
Vowel

Question 5 — Solution

dart
void main() {
  var battery = 15;
  var status = switch (battery) {
    >= 80 => 'Full',
    >= 30 => 'Good',
    _ => 'Low',
  };
  print(status);
}
output
Low

Question 6 — Solution

dart
void main() {
  var age = 65;
  var price = switch (age) {
    < 5 => 0,
    >= 5 && < 18 => 300,
    >= 60 => 400,
    _ => 800,
  };
  print('Price: $price');
}
output
Price: 400

Question 7 — Solution

dart
void main() {
  var celsius = 8;
  var feeling = switch (celsius) {
    < 10 => 'Cold',
    < 25 => 'Mild',
    _ => 'Hot',
  };
  print(feeling);
}
output
Cold

Question 8 — Solution

As written it prints Bronze, because 1200 matches >= 100 first. Put the strictest arm first:

dart
void main() {
  var points = 1200;
  var tier = switch (points) {
    >= 1000 => 'Gold',
    >= 500 => 'Silver',
    >= 100 => 'Bronze',
    _ => 'None',
  };
  print(tier);
}
output
Gold

Question 9 — Solution

The guard goes on a wildcard arm, so it applies to every city:

dart
void main() {
  var city = 'Lahore';
  var total = 7000;
  var shipping = switch (city) {
    _ when total >= 5000 => 0,
    'Karachi' => 200,
    _ => 350,
  };
  print('Shipping: $shipping');
}
output
Shipping: 0

_ when ... matches only when the guard is true, so the arms below it are still reached.

Question 10 — Solution

dart
void main() {
  int? score = int.tryParse('abc');
  var result = switch (score) {
    null => 'Invalid',
    >= 50 => 'Pass',
    _ => 'Fail',
  };
  print(result);
}
output
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

  1. What is the main difference between a switch statement and a switch expression?
  2. What symbol separates a pattern from its result? What separates two arms?
  3. What does _ match, and where should it go?
  4. What does "exhaustive" mean?
  5. Why does a switch on a bool not need _?
  6. In what order does Dart try relational patterns such as >= 90 and >= 80?
  7. What does when add to an arm?

Key Takeaways

  • A switch expression chooses and produces a value: switch (x) { a => ..., _ => ... }.
  • Arms are pattern => result, separated by commas; no case, no default, end with };.
  • Dart tries arms from top to bottom and uses the first match.
  • _ matches anything; it replaces default and goes last.
  • A switch expression must be exhaustive — every possible value covered — or it does not compile. A bool needs only true and false.
  • || puts several values in one arm; relational patterns such as >= 90 match ranges, and && joins two of them.
  • A when guard adds an extra condition that must also be true.

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.


Video lecture

Subscribe
Placeholder video — the real recording isn't linked yet.