/dart-use-pattern-matching
Use switch expressions and pattern matching where appropriate
$ npx -y skills add flutter/agent-plugins --skill dart-use-pattern-matching --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/dart-use-pattern-matching
Context preview
The summary Claude sees to decide when to auto-load this skill.
Use switch expressions and pattern matching where appropriate
SKILL.md
dart-use-pattern-matching.SKILL.mdname: dart-use-pattern-matching
description: Use switch expressions and pattern matching where appropriate
metadata:
model: models/gemini-3.1-pro-preview
last_modified: Fri, 24 Apr 2026 15:08:55 GMT
Implementing Dart Patterns
Contents
- [Pattern Selection Strategy](#pattern-selection-strategy)
- [Switch Statements vs. Expressions](#switch-statements-vs-expressions)
- [Core Pattern Implementations](#core-pattern-implementations)
- [Workflows](#workflows)
- [Examples](#examples)
Pattern Selection Strategy
Apply specific pattern types based on the data structure and desired outcome. Follow these conditional guidelines:
- **If validating and extracting from deserialized data (e.g., JSON):** Use Map and List patterns to simultaneously check structure and destructure key-value pairs.
- **If handling multiple return values:** Use Record patterns to destructure fields directly into local variables.
- **If executing type-specific behavior (Algebraic Data Types):** Use Object patterns combined with `sealed` classes to ensure exhaustiveness.
- **If matching numeric ranges or conditions:** Use Relational (`>=`, `<=`) and Logical-and (`&&`) patterns.
- **If multiple cases share logic:** Use Logical-or (`||`) patterns to share a single case body or guard clause.
- **If ignoring specific values:** Use the Wildcard pattern (`_`) or a non-matching Rest element (`...`) in collections.
Switch Statements vs. Expressions
Select the appropriate switch construct based on the execution context:
- **If producing a value:** Use a **switch expression**.
- Syntax: `switch (value) { pattern => expression, }`
- Rule: Each case must be a single expression. No implicit fallthrough. Must be exhaustive.
- **If executing statements or side effects:** Use a **switch statement**.
- Syntax: `switch (value) { case pattern: statements; }`
- Rule: Empty cases fall through to the next case. Non-empty cases implicitly break (no `break` keyword required).
Core Pattern Implementations
Implement patterns using the following syntax and rules:
- **Logical-or (`||`):** `pattern1 || pattern2`. Both branches must define the exact same set of variables.
- **Logical-and (`&&`):** `pattern1 && pattern2`. Branches must *not* define overlapping variables.
- **Relational:** `==`, `!=`, `<`, `>`, `<=`, `>=` followed by a constant expression.
- **Cast (`as`):** `pattern as Type`. Throws if the value does not match the type. Use to forcibly assert types during destructuring.
- **Null-check (`?`):** `pattern?`. Fails the match if the value is null. Binds the variable to the non-nullable base type.
- **Null-assert (`!`):** `pattern!`. Throws if the value is null.
- **Variable:** `var name` or `Type name`. Binds the matched value to a new local variable.
- **Wildcard (`_`):** Matches any value and discards it.
- **List:** `[pattern1, pattern2]`. Matches lists of exact length unless a Rest element (`...` or `...var rest`) is used.
- **Map:** `{"key": pattern}`. Matches maps containing the specified keys. Ignores unmatched keys.
- **Record:** `(pattern1, named: pattern2)`. Matches records of the exact shape. Use `:var name` to infer the getter name.
- **Object:** `ClassName(field: pattern)`. Matches instances of `ClassName`. Use `:var field` to infer the getter name.
Workflows
Task Progress: Implementing Pattern Matching
Copy this checklist to track progress when implementing complex pattern matching logic:
- [ ] Identify the data structure being evaluated (JSON, Record, Class, Enum).
- [ ] Select the appropriate switch construct (Expression for values, Statement for side-effects).
- [ ] Define the required patterns (Object, Map, List, Record).
- [ ] Extract required data using Variable patterns (`var x`, `:var y`).
- [ ] Apply Guard clauses (`when condition`) for logic that cannot be expressed via patterns.
- [ ] Handle unmatched cases using a Wildcard (`_`) or `default` clause (if not using a sealed class).
- [ ] Run exhaustiveness validator.
Feedback Loop: Exhaustiveness Checking
When switching over `sealed` classes or enums, you must ensure all subtypes are handled.
1. **Run validator:** Execute `dart analyze`. 2. **Review errors:** Look for "The type 'X' is not exhaustively matched by the switch cases" errors. 3. **Fix:** Add the missing Object patterns for the unhandled subtypes, or add a Wildcard (`_`) case if a default fallback is acceptable.
Examples
JSON Validation and Destructuring
Use Map and List patterns to validate structure and extract data in a single step.
**Input:**
var data = {
'user': ['Lily', 13],
};**Implementation:**
if (data case {'user': [String name, int age]}) {
print('User $name is $age years old.');
} else {
print('Invalid JSON structure.');
}Algebraic Data Types (Sealed Classes)
Use Object patterns with switch expressions to handle family types exhaustively.
**Implementation:**
sealed class Shape {}
class Square implements Shape {
final double length;
Square(this.length);
}
class Circle implements Shape {
final double radius;
Circle(this.radius);
}
// Switch expression guarantees exhaustiveness due to `sealed` modifier.
double calculateArea(Shape shape) => switch (shape) {
Square(length: var l) => l * l,
Circle(:var radius) => math.pi * radius * radius,
};Variable Swapping and Destructuring
Use variable assignment patterns to swap values or extract record fields without temporary variables.
**Implementation:**
var (a, b) = ('left', 'right');
(b, a) = (a, b); // Swap values
// Destructuring a function return
var (name, age) = getUserInfo();Guard Clauses and Logical-or
Use `when` to evaluate arbitrary conditions after a pattern matches.
**Implementation:**
switch (shape) {
case Square(size: var s) || Circle(size: var s) when s > 0:
print('Valid symmetric shape with size $s');
case Square()Read more
name: dart-use-pattern-matching description: Use switch expressions and pattern matching where appropriate metadata: model: models/gemini-3.1-pro-preview last_modified: Fri, 24 Apr 2026 15:08:55 GMT
Implementing Dart Patterns
Contents
- [Pattern Selection Strategy](#pattern-selection-strategy)
- [Switch Statements vs. Expressions](#switch-statements-vs-expressions)
- [Core Pattern Implementations](#core-pattern-implementations)
- [Workflows](#workflows)
- [Examples](#examples)
Pattern Selection Strategy
Apply specific pattern types based on the data structure and desired outcome. Follow these conditional guidelines:
- **If validating and extracting from deserialized data (e.g., JSON):** Use Map and List patterns to simultaneously check structure and destructure key-value pairs.
- **If handling multiple return values:** Use Record patterns to destructure fields directly into local variables.
- **If executing type-specific behavior (Algebraic Data Types):** Use Object patterns combined with `sealed` classes to ensure exhaustiveness.
- **If matching numeric ranges or conditions:** Use Relational (`>=`, `<=`) and Logical-and (`&&`) patterns.
- **If multiple cases share logic:** Use Logical-or (`||`) patterns to share a single case body or guard clause.
- **If ignoring specific values:** Use the Wildcard pattern (`_`) or a non-matching Rest element (`...`) in collections.
Switch Statements vs. Expressions
Select the appropriate switch construct based on the execution context:
- **If producing a value:** Use a **switch expression**.
- Syntax: `switch (value) { pattern => expression, }`
- Rule: Each case must be a single expression. No implicit fallthrough. Must be exhaustive.
- **If executing statements or side effects:** Use a **switch statement**.
- Syntax: `switch (value) { case pattern: statements; }`
- Rule: Empty cases fall through to the next case. Non-empty cases implicitly break (no `break` keyword required).
Core Pattern Implementations
Implement patterns using the following syntax and rules:
- **Logical-or (`||`):** `pattern1 || pattern2`. Both branches must define the exact same set of variables.
- **Logical-and (`&&`):** `pattern1 && pattern2`. Branches must *not* define overlapping variables.
- **Relational:** `==`, `!=`, `<`, `>`, `<=`, `>=` followed by a constant expression.
- **Cast (`as`):** `pattern as Type`. Throws if the value does not match the type. Use to forcibly assert types during destructuring.
- **Null-check (`?`):** `pattern?`. Fails the match if the value is null. Binds the variable to the non-nullable base type.
- **Null-assert (`!`):** `pattern!`. Throws if the value is null.
- **Variable:** `var name` or `Type name`. Binds the matched value to a new local variable.
- **Wildcard (`_`):** Matches any value and discards it.
- **List:** `[pattern1, pattern2]`. Matches lists of exact length unless a Rest element (`...` or `...var rest`) is used.
- **Map:** `{"key": pattern}`. Matches maps containing the specified keys. Ignores unmatched keys.
- **Record:** `(pattern1, named: pattern2)`. Matches records of the exact shape. Use `:var name` to infer the getter name.
- **Object:** `ClassName(field: pattern)`. Matches instances of `ClassName`. Use `:var field` to infer the getter name.
Workflows
Task Progress: Implementing Pattern Matching
Copy this checklist to track progress when implementing complex pattern matching logic:
- [ ] Identify the data structure being evaluated (JSON, Record, Class, Enum).
- [ ] Select the appropriate switch construct (Expression for values, Statement for side-effects).
- [ ] Define the required patterns (Object, Map, List, Record).
- [ ] Extract required data using Variable patterns (`var x`, `:var y`).
- [ ] Apply Guard clauses (`when condition`) for logic that cannot be expressed via patterns.
- [ ] Handle unmatched cases using a Wildcard (`_`) or `default` clause (if not using a sealed class).
- [ ] Run exhaustiveness validator.
Feedback Loop: Exhaustiveness Checking
When switching over `sealed` classes or enums, you must ensure all subtypes are handled.
1. **Run validator:** Execute `dart analyze`. 2. **Review errors:** Look for "The type 'X' is not exhaustively matched by the switch cases" errors. 3. **Fix:** Add the missing Object patterns for the unhandled subtypes, or add a Wildcard (`_`) case if a default fallback is acceptable.
Examples
JSON Validation and Destructuring
Use Map and List patterns to validate structure and extract data in a single step.
**Input:**
var data = {
'user': ['Lily', 13],
};**Implementation:**
if (data case {'user': [String name, int age]}) {
print('User $name is $age years old.');
} else {
print('Invalid JSON structure.');
}Algebraic Data Types (Sealed Classes)
Use Object patterns with switch expressions to handle family types exhaustively.
**Implementation:**
sealed class Shape {}
class Square implements Shape {
final double length;
Square(this.length);
}
class Circle implements Shape {
final double radius;
Circle(this.radius);
}
// Switch expression guarantees exhaustiveness due to `sealed` modifier.
double calculateArea(Shape shape) => switch (shape) {
Square(length: var l) => l * l,
Circle(:var radius) => math.pi * radius * radius,
};Variable Swapping and Destructuring
Use variable assignment patterns to swap values or extract record fields without temporary variables.
**Implementation:**
var (a, b) = ('left', 'right');
(b, a) = (a, b); // Swap values
// Destructuring a function return
var (name, age) = getUserInfo();Guard Clauses and Logical-or
Use `when` to evaluate arbitrary conditions after a pattern matches.
**Implementation:**
switch (shape) {
case Square(size: var s) || Circle(size: var s) when s > 0:
print('Valid symmetric shape with size $s');
case Square()Agent plugins for Flutter, maintained by the Flutter team. A collection of plugins designed to extend AI agent capabilities for Flutter development.
Other skills on dart-flutter.
- /dart-add-unit-test
Write and organize unit tests for functions, methods, and classes using `package:test`. Use when creating new logic or fixing bugs to ensure code remains correct and regression-free.
Open skill - /dart-build-cli-app
Entrypoint structure, exit codes, cross-platform scripts. Use when building command line utilities, scripts, or applications.
Open skill - /dart-collect-coverage
Collect coverage using the coverage packge and create an LCOV report
Open skill - /dart-fix-runtime-errors
Uses get_runtime_errors and lsp to fetch an active stack trace, locate the failing line, apply a fix, and verify resolution via hot_reload.
Open skill - /dart-generate-test-mocks
Define and generate mock objects for external dependencies using `package:mockito` and `build_runner`. Use when unit testing classes that depend on complex external services like APIs or databases.
Open skill - /dart-migrate-to-checks-package
Replace the usage of `expect` and similar functions from `package:matcher` to `package:checks` equivalents.
Open skill

