| name | dart-use-pattern-matching |
| description | Leverage switch expressions and Dart 3+ pattern matching to build clean, exhaustive, and type-safe control flows for algebraic data types, JSON parsing, and variable destructuring. |
| metadata | {"platforms":"dart","languages":"dart","category":"logic"} |
Contents
Pattern Selection Strategy
Apply distinct pattern matching strategies based on the target data structure:
- JSON Validation and Destructuring: Use Map and List patterns to simultaneously validate nested shapes and bind values to local variables in a single step.
- Multiple Returns: Return Records from functions and use Record patterns to destructure named or positional values directly into scoped variables.
- Algebraic Data Types (ADTs): Combine
sealed class hierarchies with Object patterns in switch expressions to benefit from compile-time exhaustiveness checking.
- Numeric Ranges and logical Checks: Combine relational patterns (
>=, <, etc.) and logical-and (&&) or logical-or (||) operators within switch cases.
- Guard Clauses: Use the
when keyword to assert dynamic conditions that cannot be expressed purely through static patterns.
Switch Expressions vs Switch Statements
Choose the correct switch construct based on the operational context:
Switch Expressions
- When to Use: When the switch construct must resolve to and return a single value.
- Syntax:
final result = switch (value) { pattern => expression, _ => fallback };
- Rule: Every case must resolve to an expression (no statements or code blocks). The cases must be exhaustive (checked by the compiler). Implicit fallthrough is not supported.
Switch Statements
Core Pattern Syntaxes
Understand and apply standard Dart 3+ pattern syntaxes:
- Logical-or (
||): Combines patterns. Matches if either pattern succeeds. Both branches must define identical sets of variables.
- Logical-and (
&&): Matches if both patterns succeed. Branches must not define overlapping variables.
- Relational: Evaluates against constants using comparative operators (e.g.
> 100, != 0).
- Cast (
as): Asserts type transformations during destructuring (throws if type check fails).
- Null-check (
?): Filters out null values and binds variables to their non-nullable base types.
- Null-assert (
!): Asserts that a value is non-nullable (throws if null).
- List Pattern: Matches arrays of specific length (e.g.
[var first, var second]). Use a rest element (...) to ignore trailing list elements.
- Map Pattern: Matches key-value targets (e.g.
{'id': var id}). Ignores keys that are not explicitly matched.
- Object Pattern: Matches class instances and extracts property values (e.g.
User(:final name)).
Workflow: Designing Pattern Matching Logic
Follow this checklist to implement clean pattern matching structures:
Examples
JSON Validation and Parsing
Validate structure and extract values in a single step using map destructuring.
void processPayload(Map<String, dynamic> payload) {
if (payload case {'status': 'success', 'data': [String title, int count]}) {
print('Product: $title, Inventory: $count');
} else {
print('Invalid payload structure received');
}
}
Exhaustive Sealed Class Matching
Leverage sealed classes to force exhaustive compile-time verification when executing domain logic.
sealed class NetworkState {}
class NetworkIdle extends NetworkState {}
class NetworkLoading extends NetworkState {}
class NetworkSuccess extends NetworkState {
final String response;
NetworkSuccess(this.response);
}
class NetworkFailure extends NetworkState {
final String error;
NetworkFailure(this.error);
}
// Compiler guarantees all sub-states are handled without requiring a default fallback
String getStatusMessage(NetworkState state) {
return switch (state) {
NetworkIdle() => 'Waiting to connect...',
NetworkLoading() => 'Loading data...',
NetworkSuccess(:final response) => 'Data loaded: $response',
NetworkFailure(:final error) => 'Failed: $error',
};
}
Record Destructuring and Swapping
// Destructuring a positional and named return record:
(String name, {int age}) getUser() => ('Alice', age: 30);
void main() {
final (name, age: userAge) = getUser();
print('$name is $userAge years old');
// Value swapping without temp variables:
var (x, y) = (1, 2);
(y, x) = (x, y);
}