Patterns and Pattern Matching

20.1 Where Patterns Appear

switch statements/expressions, if-case, variable declarations (destructuring), for-in loops, and assignments.

20.2 Pattern Types

case 404:, case 'admin':, case null: case var x:, case final String s: case >= 0 && < 10:, case != 0:

case 'a' || 'b':, case >= 1 && <= 5: case var x as int:

case var x?: (matches non-null, binds non-nullable) case var x!: (throws if null) case [a, b,...rest]:, case [_, _]: case {'name': String name}:

case (int x, int y):, case (:var name,:var age): case Circle(radius: var r):, case Point(:var x,:var y): case (1 || 2) && != 3:

20.3 Examples

Exhaustiveness checking: the compiler verifies that switch covers all cases for bool, enums, sealed types, records, and nullable types.

String describe(Object? value) => switch (value) {
      null => 'null',
      int n when n < 0 => 'negative int $n',
      int n => 'int $n',
      double() => 'a double',
      String s when s.isEmpty => 'empty string',
      String s => 'string "$s"',
      [] => 'empty list',
      [var first, ...] => 'list starting with $first',
      {'type': 'user', 'name': String name} => 'user $name',
      (int x, int y) => 'point ($x, $y)',
      _ => 'something else',
    };

// Destructuring JSON safely
void handle(Map<String, Object?> json) {
  if (json case {'user': {'name': String name, 'age': int age}}) {
    print('$name ($age)');
  } else {
    print('Invalid JSON');
  }
}

// Destructuring in declarations and loops
final [first, second, ...others] = [1, 2, 3, 4, 5];
final {'host': host, 'port': port} = {'host': 'localhost', 'port': 8080};
for (final (index, value) in ['a', 'b'].indexed) print('$index=$value');

// Object patterns with getters
class Point { final int x, y; const Point(this.x, this.y); }
String quadrant(Point p) => switch (p) {
      Point(x: 0, y: 0) => 'origin',
      Point(x: > 0, y: > 0) => 'Q1',
      Point(x: < 0, y: > 0) => 'Q2',
      Point(:var x, :var y)
…