$npx -y skills add flutter/agent-plugins --skill dart-use-pattern-matchingUse switch expressions and pattern matching where appropriate
| 1 | # Implementing Dart Patterns |
| 2 | |
| 3 | ## Contents |
| 4 | - [Pattern Selection Strategy](#pattern-selection-strategy) |
| 5 | - [Switch Statements vs. Expressions](#switch-statements-vs-expressions) |
| 6 | - [Core Pattern Implementations](#core-pattern-implementations) |
| 7 | - [Workflows](#workflows) |
| 8 | - [Examples](#examples) |
| 9 | |
| 10 | ## Pattern Selection Strategy |
| 11 | |
| 12 | Apply specific pattern types based on the data structure and desired outcome. Follow these conditional guidelines: |
| 13 | |
| 14 | * **If validating and extracting from deserialized data (e.g., JSON):** Use Map and List patterns to simultaneously check structure and destructure key-value pairs. |
| 15 | * **If handling multiple return values:** Use Record patterns to destructure fields directly into local variables. |
| 16 | * **If executing type-specific behavior (Algebraic Data Types):** Use Object patterns combined with `sealed` classes to ensure exhaustiveness. |
| 17 | * **If matching numeric ranges or conditions:** Use Relational (`>=`, `<=`) and Logical-and (`&&`) patterns. |
| 18 | * **If multiple cases share logic:** Use Logical-or (`||`) patterns to share a single case body or guard clause. |
| 19 | * **If ignoring specific values:** Use the Wildcard pattern (`_`) or a non-matching Rest element (`...`) in collections. |
| 20 | |
| 21 | ## Switch Statements vs. Expressions |
| 22 | |
| 23 | Select the appropriate switch construct based on the execution context: |
| 24 | |
| 25 | * **If producing a value:** Use a **switch expression**. |
| 26 | * Syntax: `switch (value) { pattern => expression, }` |
| 27 | * Rule: Each case must be a single expression. No implicit fallthrough. Must be exhaustive. |
| 28 | * **If executing statements or side effects:** Use a **switch statement**. |
| 29 | * Syntax: `switch (value) { case pattern: statements; }` |
| 30 | * Rule: Empty cases fall through to the next case. Non-empty cases implicitly break (no `break` keyword required). |
| 31 | |
| 32 | ## Core Pattern Implementations |
| 33 | |
| 34 | Implement patterns using the following syntax and rules: |
| 35 | |
| 36 | * **Logical-or (`||`):** `pattern1 || pattern2`. Both branches must define the exact same set of variables. |
| 37 | * **Logical-and (`&&`):** `pattern1 && pattern2`. Branches must *not* define overlapping variables. |
| 38 | * **Relational:** `==`, `!=`, `<`, `>`, `<=`, `>=` followed by a constant expression. |
| 39 | * **Cast (`as`):** `pattern as Type`. Throws if the value does not match the type. Use to forcibly assert types during destructuring. |
| 40 | * **Null-check (`?`):** `pattern?`. Fails the match if the value is null. Binds the variable to the non-nullable base type. |
| 41 | * **Null-assert (`!`):** `pattern!`. Throws if the value is null. |
| 42 | * **Variable:** `var name` or `Type name`. Binds the matched value to a new local variable. |
| 43 | * **Wildcard (`_`):** Matches any value and discards it. |
| 44 | * **List:** `[pattern1, pattern2]`. Matches lists of exact length unless a Rest element (`...` or `...var rest`) is used. |
| 45 | * **Map:** `{"key": pattern}`. Matches maps containing the specified keys. Ignores unmatched keys. |
| 46 | * **Record:** `(pattern1, named: pattern2)`. Matches records of the exact shape. Use `:var name` to infer the getter name. |
| 47 | * **Object:** `ClassName(field: pattern)`. Matches instances of `ClassName`. Use `:var field` to infer the getter name. |
| 48 | |
| 49 | ## Workflows |
| 50 | |
| 51 | ### Task Progress: Implementing Pattern Matching |
| 52 | Copy this checklist to track progress when implementing complex pattern matching logic: |
| 53 | |
| 54 | - [ ] Identify the data structure being evaluated (JSON, Record, Class, Enum). |
| 55 | - [ ] Select the appropriate switch construct (Expression for values, Statement for side-effects). |
| 56 | - [ ] Define the required patterns (Object, Map, List, Record). |
| 57 | - [ ] Extract required data using Variable patterns (`var x`, `:var y`). |
| 58 | - [ ] Apply Guard clauses (`when condition`) for logic that cannot be expressed via patterns. |
| 59 | - [ ] Handle unmatched cases using a Wildcard (`_`) or `default` clause (if not using a sealed class). |
| 60 | - [ ] Run exhaustiveness validator. |
| 61 | |
| 62 | ### Feedback Loop: Exhaustiveness Checking |
| 63 | When switching over `sealed` classes or enums, you must ensure all subtypes are handled. |
| 64 | |
| 65 | 1. **Run validator:** Execute `dart analyze`. |
| 66 | 2. **Review errors:** Look for "The type 'X' is not exhaustively matched by the switch cases" errors. |
| 67 | 3. **Fix:** Add the missing Object patterns for the unhandled subtypes, or add a Wildcard (`_`) case if a default fallback is acceptable. |
| 68 | |
| 69 | ## Examples |
| 70 | |
| 71 | ### JSON Validation and Destructuring |
| 72 | Use Map and List patterns to validate structure and extract data in a single step. |
| 73 | |
| 74 | **Input:** |
| 75 | ```dart |
| 76 | var data = { |
| 77 | 'user': ['Lily', 13], |
| 78 | }; |
| 79 | ``` |
| 80 | |
| 81 | **Implementation:** |
| 82 | ```dart |
| 83 | if (data case {'user': [String name, int age]}) { |
| 84 | print('User $name is $age years old.'); |
| 85 | } else { |
| 86 | print('Invalid JSON structure.'); |
| 87 | } |
| 88 | ``` |
| 89 | |
| 90 | ### Algebraic Data Types (Sealed Classes) |
| 91 | Use Object patterns with switch expressions to handle family types exhaustively. |
| 92 | |
| 93 | **Implementation:** |