Use when writing switch statements, refactoring if-else chains, creating data classes, choosing records vs classes, destructuring values, or modernizing pre-Dart-3 code.
npx skills add https://github.com/evanca/flutter-ai-rules --skill dart-3-updates
Apply Dart 3 language features — branches, patterns, pattern types, and records — correctly and idiomatically.
Use this skill when:
switch statements or if-else chains.// Standard if
if (score >= 90) {
grade = 'A';
} else if (score >= 80) {
grade = 'B';
} else {
grade = 'C';
}
// if-case: match and destructure against a single pattern
if (pair case [int x, int y]) {
print('$x, $y');
}
if conditions must evaluate to a bool.if-case, variables declared in the pattern are scoped to the matching branch.else branch (if present).switch (command) {
case 'quit':
quit();
case 'start' || 'begin': // logical-or pattern
startGame();
default:
print('Unknown command');
}
case body executes and jumps to the end — break is not required.continue, throw, or return.default or _ to handle unmatched values.break to prevent fallthrough in an empty case.continue with a label for non-sequential fallthrough.case a || b) to share a body between cases.final color = switch (shape) {
Circle() => 'red',
Square() => 'blue',
_ => 'unknown',
};
case; use => for bodies; separate cases with commas._ (not default).switch statements and expressions at compile time.default/_, enums, or sealed types to satisfy exhaustiveness.sealed class Shape {}
class Circle extends Shape {}
class Square extends Shape {}
// Dart knows all subtypes — no default needed:
String describe(Shape s) => switch (s) {
Circle() => 'circle',
Square() => 'square',
};
switch (point) {
case (int x, int y) when x == y:
print('Diagonal: $x');
case (int x, int y):
print('$x, $y');
}
when condition after a pattern to further constrain matching.if-case, switch statements, and switch expressions.false, execution proceeds to the next case.Patterns represent the shape of a value for matching and destructuring.
// Variable declaration
var (a, [b, c]) = ('str', [1, 2]);
// Variable assignment (swap)
(b, a) = (a, b);
// for-in loop destructuring
for (final MapEntry(:key, :value) in map.entries) { ... }
// switch / if-case (see Branches section)
_ ignores parts of a matched value....) in list patterns ignore remaining elements.var Foo(:one, :two) = myFoo;
if (data case {'user': [String name, int age]}) {
print('$name, $age');
}
| Pattern | Syntax | Description |
|---|---|---|
| Logical-or | p1 \|\| p2 | Matches if any branch matches. All branches must bind the same variables. |
| Logical-and | p1 && p2 | Matches if both match. Variable names must not overlap. |
| Relational | == c, < c, >= c | Compares value to a constant. Combine with && for ranges. |
| Cast | subpattern as Type | Asserts type, then matches inner pattern. Throws if type mismatch. |
| Null-check | subpattern? | Matches non-null; binds non-nullable type. |
| Null-assert | subpattern! | Matches non-null or throws. Use in declarations to eliminate nulls. |
| Constant | 42, 'str', const Foo() | Matches if value equals the constant. |
| Variable | var name, final Type name | Binds matched value to a new variable. Typed form only matches the declared type. |
| Wildcard | _, Type _ | Matches any value without binding. |
| Parenthesized | (subpattern) | Controls precedence. |
| List | [p1, p2] | Matches lists by position. Length must match unless a rest element is used. |
| Rest element | ..., ...rest | Matches arbitrary-length tails or collects remaining elements. |
| Map | {'key': subpattern} | Matches maps by key. Missing keys throw StateError. |
| Record | (p1, p2), (x: p1, y: p2) | Matches records by shape; field names can be omitted if inferred. |
| Object | ClassName(field: p) | Matches by type and destructures via getters. Extra fields ignored. |
// Create
var record = ('first', a: 2, b: true, 'last');
// Type annotation
({int a, bool b}) namedRecord;
// Access
print(record.$1); // positional: 'first'
print(record.a); // named: 2
$1, $2, .name); no setters.hashCode and == are automatically defined.(String name, int age) userInfo(Map<String, dynamic> json) {
return (json['name'] as String, json['age'] as int);
}
var (name, age) = userInfo(json);
// Named fields:
final (:name, :age) = userInfo(json);
Use a record when:
Use a class when:
copyWith.typedef for record types to improve readability and maintainability.When modernizing pre-Dart-3 code, follow these steps:
// Before (pre-Dart 3)
String label;
if (status == Status.loading) {
label = 'Loading...';
} else if (status == Status.success) {
label = 'Done';
} else {
label = 'Error';
}
// After (Dart 3)
final label = switch (status) {
Status.loading => 'Loading...',
Status.success => 'Done',
Status.error => 'Error',
};
// Before
abstract class Result {}
class Success extends Result { final String data; Success(this.data); }
class Failure extends Result { final String error; Failure(this.error); }
// After — enables exhaustive switch
sealed class Result {}
final class Success extends Result { const Success(this.data); final String data; }
final class Failure extends Result { const Failure(this.error); final String error; }
Replace wrapper classes used solely for returning multiple values with records.
Run dart analyze to confirm exhaustiveness and type safety after each change.
Behavioral guidelines to reduce common LLM coding mistakes. Use when writing, reviewing, or refactoring code to avoid overcomplication, make surgical changes, surface assumptions, and define verifiable success criteria.
Structured task planning with clear breakdowns, dependencies, and verification criteria. Use when implementing features, refactoring, or any multi-step work.
Master ES6+ features including async/await, destructuring, spread operators, arrow functions, promises, modules, iterators, generators, and functional programming patterns for writing clean, efficient JavaScript code. Use when refactoring legacy code, implementing modern patterns, or optimizing JavaScript applications.
Master ES6+ features including async/await, destructuring, spread operators, arrow functions, promises, modules, iterators, generators, and functional programming patterns for writing clean, efficient JavaScript code. Use when refactoring legacy code, implementing modern patterns, or optimizing JavaScript applications.
Guidelines and format for writing pull request descriptions in this repository. Use this skill whenever the user asks you to draft a pull request description, submit a PR, or update a PR description.
Angular performance optimization and best practices guide. Use when writing, reviewing, or refactoring Angular code for optimal performance, bundle size, and rendering efficiency.
Use when a user asks to debug or fix failing GitHub PR checks that run in GitHub Actions. Uses `gh` to inspect checks and logs, summarize failure context, draft a fix plan, and implement only after explicit approval. Treats external providers (for example Buildkite) as out of scope and reports only the details URL. Do NOT use for addressing PR review comments (use gh-address-comments) or general CI outside GitHub Actions.
Angular performance optimization and best practices guide. Use when writing, reviewing, or refactoring Angular code for optimal performance, bundle size, and rendering efficiency.
Take evanca/dart-3-updates from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.