Skip to content
AgileLogics
6 min read

Patterns and Destructuring

Prerequisites: Records, switch statements and expressions

Patterns (Dart 3+) describe the shape of a value. They let you check that shape and pull out the parts you need in one step — destructuring.

Destructuring in declarations

void main() {
  final (name, age) = ('Ada', 36);
  print('$name is $age');

  final [first, second, ...rest] = [1, 2, 3, 4, 5];
  print('$first $second $rest');

  final {'host': host, 'port': port} = {'host': 'localhost', 'port': 8080};
  print('$host:$port');

  var a = 1;
  var b = 2;
  (a, b) = (b, a);
  print('$a $b');
}
Ada is 36
1 2 [3, 4, 5]
localhost:8080
2 1
  • ...rest collects the remaining list elements.
  • Map patterns pull out values by key.
  • (a, b) = (b, a) swaps two variables in one line.

Patterns in switch

Patterns are most powerful in switch, where they match structure:

String describe(Object command) => switch (command) {
      ['add', String item] => 'Adding $item',
      ['move', String from, String to] => 'Moving $from to $to',
      [] => 'Empty command',
      [String cmd, ...] => 'Unknown command: $cmd',
      (int x, int y) when x == y => 'A diagonal point',
      (int x, int y) => 'Point at $x, $y',
      _ => 'Not a command',
    };

void main() {
  print(describe(['add', 'milk']));
  print(describe(['move', 'a', 'b']));
  print(describe(['jump', 'high']));
  print(describe((2, 2)));
  print(describe((1, 5)));
  print(describe(42));
}
Adding milk
Moving a to b
Unknown command: jump
A diagonal point
Point at 1, 5
Not a command

Validating JSON-like data with if-case

if (value case pattern) checks a single pattern — ideal for data from outside your program:

void main() {
  final Object json = {'user': {'name': 'Ada', 'age': 36}};

  if (json case {'user': {'name': String name, 'age': int age}}) {
    print('$name is $age');
  } else {
    print('Unexpected shape');
  }
}
Ada is 36

One pattern checks that json is a map, has a user map inside, and that name is a String and age is an int — then binds both.

Object patterns

ClassName(field: pattern) matches an object and reads its fields:

class Point {
  final int x, y;
  const Point(this.x, this.y);
}

void main() {
  const p = Point(0, 7);
  final label = switch (p) {
    Point(x: 0, y: 0) => 'origin',
    Point(x: 0) => 'on the y-axis',
    Point(:final x, :final y) => 'at $x, $y',
  };
  print(label);
}
on the y-axis

:final x is shorthand for x: final x.