Skip to content
Development
Skill

/dart-3-updates

Use when writing switch statements, refactoring if-else chains, creating data classes, choosing records vs classes, destructuring values, or modernizing pre-Dart-3 code.

From plugin
flutter-ai-skills
63937 skills
Install
$ npx -y skills add evanca/flutter-ai-rules --skill dart-3-updates --agent claude-code

How 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-3-updates

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when writing switch statements, refactoring if-else chains, creating data classes, choosing records vs classes, destructuring values, or modernizing pre-Dart-3 code.

SKILL.md

dart-3-updates.SKILL.md
name: dart-3-updates
description: "Use when writing switch statements, refactoring if-else chains, creating data classes, choosing records vs classes, destructuring values, or modernizing pre-Dart-3 code."
license: MIT

Dart 3 Updates Skill

Apply Dart 3 language features — branches, patterns, pattern types, and records — correctly and idiomatically.

When to Use

Use this skill when:

  • Writing or refactoring `switch` statements or `if-else` chains.
  • Creating new data-holding classes and deciding between sealed classes, records, or plain classes.
  • Destructuring values from maps, lists, records, or objects.
  • Modernizing pre-Dart-3 code to use patterns, exhaustiveness checks, or switch expressions.

---

1. Branches

if / if-case

// 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`.
  • In `if-case`, variables declared in the pattern are scoped to the matching branch.
  • If the pattern does not match, control flows to the `else` branch (if present).

switch statements

switch (command) {
  case 'quit':
    quit();
  case 'start' || 'begin': // logical-or pattern
    startGame();
  default:
    print('Unknown command');
}
  • Each matched `case` body executes and jumps to the end — `break` is **not required**.
  • Non-empty cases can end with `continue`, `throw`, or `return`.
  • Use `default` or `_` to handle unmatched values.
  • Empty cases fall through; use `break` to prevent fallthrough in an empty case.
  • Use `continue` with a label for non-sequential fallthrough.
  • Use logical-or patterns (`case a || b`) to share a body between cases.

switch expressions

final color = switch (shape) {
  Circle() => 'red',
  Square() => 'blue',
  _ => 'unknown',
};
  • Omit `case`; use `=>` for bodies; separate cases with commas.
  • Default must use `_` (not `default`).
  • Produces a value.

Exhaustiveness

  • Dart checks exhaustiveness in `switch` statements and expressions at compile time.
  • Use `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',
};

Guard clauses

switch (point) {
  case (int x, int y) when x == y:
    print('Diagonal: $x');
  case (int x, int y):
    print('$x, $y');
}
  • Add `when condition` after a pattern to further constrain matching.
  • Usable in `if-case`, `switch` statements, and `switch` expressions.
  • If the guard is `false`, execution proceeds to the next case.

---

2. Patterns

Patterns represent the **shape** of a value for matching and destructuring.

Uses

// 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)
  • Wildcard `_` ignores parts of a matched value.
  • Rest elements (`...`) in list patterns ignore remaining elements.
  • Case patterns are **refutable**: if no match, execution continues to the next case.
  • Destructured values in a case become local variables scoped to that case body.

Object patterns

var Foo(:one, :two) = myFoo;

JSON / nested data validation

if (data case {'user': [String name, int age]}) {
  print('$name, $age');
}

---

3. Pattern Types

| 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. |

  • Use parentheses to group lower-precedence patterns.
  • All pattern types can be **nested and combined**.

---

4. Records

// 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
  • Records are **anonymous, immutable, fixed-size** aggregates.
  • Each field can have a different type (heterogeneous).
  • Fields are accessed via built-in getters (`$1`, `$2`, `.name`); no setters.
  • Two records are equal if they have the same shape and equal field values.
  • `hashCode` and `==` are automatically defined.

Multiple return values

(String name, int age) userInfo(Map<String, dynamic> json) {
  return (json['name'] as String, json['age'] as int);
}

va
Read more
Ships withflutter-ai-skills

36 Flutter and Dart skills your coding agent loads by itself, sourced only from official documentation. A skill is a folder with a SKILL.md file.

Get the whole plugin
Stats
639
Stars
66
Forks
Active
Maintenance
Shell
Language
MIT
License
2d ago
Last commit
1y ago
Created

Repo: evanca/flutter-ai-rules

Other skills on flutter-ai-skills.