Skip to content
Development
Skill

/dart-use-pattern-matching

Applies Dart 3 pattern matching, switch expressions, and destructuring idiomatically to validate data schemas, handle algebraic data types, and decompose control flow. Use when refactoring complex if-else chains, parsing polymorphic JSON or API responses, destructuring Lists,

From plugin
agent-plugins
3.1k25 skills

Claude Code

$ npx -y skills add flutter/agent-plugins --skill dart-use-pattern-matching --agent claude-code

SKILL.md

dart-use-pattern-matching.SKILL.md
name: dart-use-pattern-matching
description: >-
  Applies Dart 3 pattern matching, switch expressions, and destructuring
  idiomatically to validate data schemas, handle algebraic data types, and
  decompose control flow. Use when refactoring complex if-else chains,
  parsing polymorphic JSON or API responses, destructuring Lists, URI/path
  segments, String.split() tokens, Records, or Maps, or enforcing exhaustiveness
  on sealed classes. Don't use for simple boolean conditions, single-variable
  type promotion (use `is`), or basic collection filtering.
metadata:
  model: models/gemini-3.1-pro-preview
  last_modified: Tue, 13 Oct 2026 19:45:00 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)
  • [Pragmatic Balance & Anti-Patterns](#pragmatic-balance--anti-patterns)
  • [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, List, and Object patterns to validate schema structure and destructure properties in a single step.
  • **If inspecting URL/path segments (`uri.pathSegments`, `p.split(path)`) or `String.split()` tokens:** Use List patterns with rest elements (`['api', 'comments', ...]`, `['assets', ...final rest]`, `[..., final parent, _]`) instead of manual `.length` checks, `.first`, `.skip(1)`, or `length - N` index arithmetic.
  • **If handling polymorphic payloads or responses:** Use `switch` expressions over map discriminant keys to deserialize into `sealed` class hierarchies.
  • **If handling multiple return values:** Use Record patterns to destructure fields directly into local variables.
  • **If unpacking multiple fields from an object or `MapEntry`:** Use Object patterns (`final Spec(:field1, :field2!) = spec;`, `for (final MapEntry(:key, :value) in map.entries)`) when promoting nullable properties or unpacking 3+ fields used across multiple statements, rather than for single-call passthroughs.
  • **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 within switch arms.
  • **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.

Pragmatic Balance & Anti-Patterns

Pattern matching and switch expressions should simplify code, not add syntactic overhead. Observe the following boundaries:

1. Prefer `is` Type Promotion over `if-case` for Single Promotable Variables

When checking or promoting a single variable, use standard `is` checks instead of `if-case` patterns that introduce shadow aliases.

  • **Prefer:**
    // ✅ Promotes `key` directly in-place without extra variables
    for (final MapEntry(:key, :value) in map.entries) {
      if (key is String && value != null) {
        process(key, value);
      }
    }
  • **Avoid:**
    // ❌ Anti-pattern: Introduces unnecessary alias variable `k`
    for (final MapEntry(:key, :value) in map.entries) {
      if (key case final String k when value != null) {
        process(k, value);
      }
    }

2. Consolidate Nullable Types in Switch Arms

When mapping or returning values where both `null` and a type `T` are valid and handled identically, match the nullable type `T?` directly rather than creating redundant `null` arms.

  • **Prefer:**
    // ✅ Clean nullable pattern match
    switch (value) {
      final String? s => s,
      _ => throw FormatException('Invalid value: $value'),
Read more
@flutter
Ships withagent-plugins

Agent plugins for Flutter, maintained by the Flutter team. A collection of plugins designed to extend AI agent capabilities for Flutter development.

Stars on GitHub

Momentum

stars in total
3.1k
Star history of flutter/agent-plugins on GitHub, a chart by star-history.com

Other skills on agent-plugins.

dart-add-unit-test

dart-add-unit-test

Write and organize unit tests for functions, methods, and classes using `package:test`. Use when creating new logic or fixing…

dart-build-cli-app

dart-build-cli-app

Architectural patterns, entrypoint structure, exit codes, stream routing, and subprocess spawning for Dart command-line interface…

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills2w309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react5d252k
@facebook@facebookInstall Skill

Questions about dart-use-pattern-matching.

Which coding agents can use dart-use-pattern-matching?
dart-use-pattern-matching installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.