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)
…