Lesson 26 minNEW

Pattern Matching Deep Dive

00:00 / 00:00

What is Pattern Matching?

Pattern matching lets you simultaneously test the structure/type of a value and extract data from it. It is the evolution of if-else chains and switch statements into something far more expressive and powerful.

Dart 3 brought patterns to switch expressions, if statements, variable declarations, and for loops. This is the single biggest syntactic improvement in Dart's history.

Pattern Types

dart
// 1. Literal patterns — match exact values switch (status) { case 200: print('OK'); case 404: print('Not Found'); case 500: print('Server Error'); } // 2. Type patterns — match by type and cast switch (shape) { case Circle c: print('Circle r=${c.radius}'); case Rectangle r: print('Rect ${r.width}x${r.height}'); } // 3. Wildcard — match anything, discard switch (value) { case int _: print('some int'); case _: print('anything else'); } // 4. Variable patterns — extract into a new variable switch (point) { case Point(x: var x, y: var y): print('($x, $y)'); }

Guard Clauses with when

dart
int classify(int n) => switch (n) { int x when x < 0 => -1, // negative 0 => 0, // zero int x when x > 0 => 1, // positive _ => throw StateError('impossible'), };

Sealed Classes + Switch = Exhaustive Matching

dart
sealed class NetworkState { } class Loading extends NetworkState { } class Success extends NetworkState { final String data; Success(this.data); } class Error extends NetworkState { final String message; Error(this.message); } // Compiler GUARANTEES all cases are handled — no default needed Widget buildUI(NetworkState state) => switch (state) { Loading() => const CircularProgressIndicator(), Success(:final data) => Text(data), Error(:final message) => Text('Error: $message', style: TextStyle(color: Colors.red)), };

Patterns in Variable Declarations

dart
// Record pattern in variable declaration final (x, y) = getCoordinates(); // Map pattern final {'name': String name, 'age': int age} = userJson; // List pattern with rest final [head, ...tail] = [1, 2, 3, 4, 5]; print(head); // 1 print(tail); // [2, 3, 4, 5] // Object pattern with field extraction final Point(:x, :y) = somePoint;

Patterns in for Loops

dart
final pairs = [(1, 'one'), (2, 'two'), (3, 'three')]; // Destructure each record in the loop for (final (number, word) in pairs) { print('$number = $word'); } // With a map final scores = {'Alice': 95, 'Bob': 87}; for (final MapEntry(:key, :value) in scores.entries) { print('$key scored $value'); }

Real-World: Parsing API Responses

dart
Future<User> fetchUser(String id) async { final response = await apiClient.get('/users/$id'); return switch (response) { {'status': 200, 'data': Map<String, dynamic> data} => User.fromJson(data), {'status': 404} => throw NotFoundException('User $id not found'), {'status': int code, 'message': String msg} => throw ApiException(code, msg), _ => throw ApiException(0, 'Unexpected response format'), }; }

Summary

Pattern matching transforms complex conditional logic into declarative, readable code. Combined with sealed classes, it gives you compile-time guarantees that every possible state is handled — eliminating an entire category of runtime bugs. This is the future of Dart control flow.

WhatsApp