Lesson 18 minNEW

switch, cases & Pattern Matching

00:00 / 00:00

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:

dart
void 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.

dart
void 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.

dart
void 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:

dart
void 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.

dart
void 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 : and break
  • Each arm is separated by a comma ,
  • _ is the wildcard that matches anything (replaces default)
  • 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

dart
void 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:

dart
class 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

dart
void 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

dart
void 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

SituationPrefer
Checking many values of the same variableswitch
Complex boolean conditions (&&, ||)if-else
Range checks (> 10, < 100)if-else
Producing a value from multiple casesswitch expression
Type and structure matching (Dart 3)switch with patterns

🔹 Real-World Examples

Example 1: Day of Week

dart
String 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

dart
String 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

dart
sealed 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

FeatureDescription
switch statementChecks a value against multiple cases
defaultFallback when no case matches
No fall-throughDart stops after matching case automatically
Empty case stackingGroup cases sharing the same body
switch expression (Dart 3)Returns a value using => syntax
_ wildcardMatches anything (replaces default)
Type/Object/List patternsMatch by structure and type (Dart 3)
when guardAdd extra conditions to a pattern
|| in patternsMatch 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!

WhatsApp