switch, cases & Pattern Matching
switch, cases & Pattern Matching
When you have many branches based on the same value, a chain of if-else if can become repetitive and hard to read. Dart's switch statement (and the powerful switch expression in Dart 3+) gives you a cleaner, more expressive way to handle multiple cases — including advanced pattern matching.
🔹 Traditional switch Statement
The classic switch checks a value against a series of case labels:
dartvoid main() { int day = 3; switch (day) { case 1: print('Monday'); break; case 2: print('Tuesday'); break; case 3: print('Wednesday'); break; case 4: print('Thursday'); break; case 5: print('Friday'); break; case 6: print('Saturday'); break; case 7: print('Sunday'); break; default: print('Invalid day'); } }
Output: Wednesday
🔹 The default Case
The default block runs when no case matches — similar to the final else in an if-else chain. Always include it to handle unexpected values safely.
dartvoid describeStatus(String status) { switch (status) { case 'active': print('Account is active.'); break; case 'suspended': print('Account is suspended.'); break; case 'deleted': print('Account has been deleted.'); break; default: print('Unknown status: $status'); } }
🔹 No Automatic Fall-Through
In C or Java, if you forget a break, execution falls through to the next case. Dart is safer — it does not fall through by default.
dartvoid main() { int x = 1; switch (x) { case 1: print('One'); // No break needed — Dart stops here automatically case 2: print('Two'); } }
Note: In Dart 3+, empty cases can fall through intentionally (see below). But a case with statements does not fall through without explicit use of continue with a label.
Grouping Cases (Intentional Fall-Through)
You can stack empty cases to share the same body:
dartvoid main() { String day = 'Saturday'; switch (day) { case 'Saturday': case 'Sunday': print('It is the weekend!'); break; default: print('It is a weekday.'); } }
🔹 switch Expression (Dart 3+)
Dart 3 introduced switch expressions — a concise way to compute a value using a switch. This is extremely useful for assignments and return statements.
dartvoid main() { int day = 6; String dayName = switch (day) { 1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday', _ => 'Invalid day', // _ is the wildcard / default }; print(dayName); // Saturday }
Key differences from the statement form:
- Uses
=>(fat arrow) instead of:andbreak - Each arm is separated by a comma
, _is the wildcard that matches anything (replacesdefault)- The whole thing is an expression — it produces a value
🔹 Pattern Matching with switch (Dart 3+)
Dart 3's switch is much more powerful — it supports patterns that can match structure, types, and values simultaneously.
Type Patterns
dartvoid describe(Object obj) { switch (obj) { case int n when n < 0: print('Negative integer: $n'); case int n: print('Positive integer: $n'); case String s: print('String of length ${s.length}: $s'); case bool b: print('Boolean: $b'); default: print('Something else: $obj'); } } void main() { describe(-5); // Negative integer: -5 describe(42); // Positive integer: 42 describe('hello'); // String of length 5: hello describe(true); // Boolean: true }
The when keyword adds an extra guard condition to a pattern.
Object Patterns
Match against the structure of an object:
dartclass Point { final int x, y; const Point(this.x, this.y); } void describePoint(Point p) { switch (p) { case Point(x: 0, y: 0): print('Origin'); case Point(x: 0, y: var y): print('On the Y-axis at $y'); case Point(x: var x, y: 0): print('On the X-axis at $x'); case Point(x: var x, y: var y): print('Point at ($x, $y)'); } } void main() { describePoint(const Point(0, 0)); // Origin describePoint(const Point(0, 5)); // On the Y-axis at 5 describePoint(const Point(3, 0)); // On the X-axis at 3 describePoint(const Point(3, 4)); // Point at (3, 4) }
List Patterns
dartvoid describeList(List<int> list) { switch (list) { case []: print('Empty list'); case [int single]: print('Single element: $single'); case [int first, int second]: print('Two elements: $first and $second'); case [int first, ...]: print('Starts with $first, has more elements'); } } void main() { describeList([]); // Empty list describeList([42]); // Single element: 42 describeList([1, 2]); // Two elements: 1 and 2 describeList([1, 2, 3]); // Starts with 1, has more elements }
Record Patterns
dartvoid main() { (String, int) person = ('Alice', 30); switch (person) { case (String name, int age) when age >= 18: print('$name is an adult (age $age)'); case (String name, int age): print('$name is a minor (age $age)'); } }
🔹 When to Use switch vs if-else
| Situation | Prefer |
|---|---|
| Checking many values of the same variable | switch |
Complex boolean conditions (&&, ||) | if-else |
Range checks (> 10, < 100) | if-else |
| Producing a value from multiple cases | switch expression |
| Type and structure matching (Dart 3) | switch with patterns |
🔹 Real-World Examples
Example 1: Day of Week
dartString getDayType(String day) { return switch (day.toLowerCase()) { 'saturday' || 'sunday' => 'Weekend', 'monday' || 'tuesday' || 'wednesday' || 'thursday' || 'friday' => 'Weekday', _ => 'Invalid day', }; } void main() { print(getDayType('Monday')); // Weekday print(getDayType('Saturday')); // Weekend print(getDayType('Holiday')); // Invalid day }
Note: The || operator inside switch patterns lets you match multiple values in a single arm (Dart 3+).
Example 2: HTTP Status Codes
dartString describeHttpStatus(int code) { return switch (code) { 200 => 'OK — Request succeeded', 201 => 'Created — Resource created', 400 => 'Bad Request — Invalid input', 401 => 'Unauthorized — Login required', 403 => 'Forbidden — Access denied', 404 => 'Not Found — Resource missing', 500 => 'Internal Server Error', 503 => 'Service Unavailable', _ => 'Unknown status code: $code', }; } void main() { print(describeHttpStatus(200)); // OK — Request succeeded print(describeHttpStatus(404)); // Not Found — Resource missing print(describeHttpStatus(500)); // Internal Server Error }
Example 3: Shape Area Calculator
dartsealed class Shape {} class Circle extends Shape { final double radius; Circle(this.radius); } class Rectangle extends Shape { final double width, height; Rectangle(this.width, this.height); } class Triangle extends Shape { final double base, height; Triangle(this.base, this.height); } double calculateArea(Shape shape) { return switch (shape) { Circle(radius: var r) => 3.14159 * r * r, Rectangle(width: var w, height: var h) => w * h, Triangle(base: var b, height: var h) => 0.5 * b * h, }; } void main() { print(calculateArea(Circle(5))); // 78.53975 print(calculateArea(Rectangle(4, 6))); // 24.0 print(calculateArea(Triangle(3, 8))); // 12.0 }
Tip: When you use a sealed class, Dart knows all possible subtypes and will warn you if you forget a case in the switch — making your code exhaustive and safe!
Summary
| Feature | Description |
|---|---|
switch statement | Checks a value against multiple cases |
default | Fallback when no case matches |
| No fall-through | Dart stops after matching case automatically |
| Empty case stacking | Group cases sharing the same body |
switch expression (Dart 3) | Returns a value using => syntax |
_ wildcard | Matches anything (replaces default) |
| Type/Object/List patterns | Match by structure and type (Dart 3) |
when guard | Add extra conditions to a pattern |
|| in patterns | Match multiple values in one arm |
Switch expressions and pattern matching are among Dart 3's most exciting features. In the next lesson, we'll look at the ternary operator for even more concise conditional expressions!