---
id: gh-dart-use-pattern-matching
name: "dart-use-pattern-matching"
url: https://skills.yangsir.net/skill/gh-dart-use-pattern-matching
author: dart-lang
domain: ai-software-architecture-engineering
tags: ["dart", "pattern-matching", "code-quality", "refactoring", "software-engineering"]
install_count: 3300
rating: 4.40 (120 reviews)
github: https://github.com/dart-lang/skills/tree/main/skills/dart-use-pattern-matching
---

# dart-use-pattern-matching

> 掌握 Dart 的模式匹配和 switch 表达式，编写更简洁、可读且健壮的代码。学习如何有效地解构数据、处理多返回值，并通过穷尽性检查实现类型特定的行为。

**Stats**: 3,300 installs · 4.4/5 (120 reviews)

## Before / After 对比

### 简化数据处理逻辑

**Before**:

传统 `if-else` 链和手动类型转换导致数据验证和提取代码冗长且难以理解，增加了维护成本和出错风险。

**After**:

使用模式匹配，将复杂的数据验证和解构逻辑简化为数行，显著提升代码可读性、简洁性和开发效率。

| Metric | Before | After | Change |
|---|---|---|---|
| 数据处理代码行数 | 15行 | 5行 | -66% |

### 提升代码健壮性与减少运行时错误

**Before**:

缺乏穷尽性检查，容易遗漏处理特定数据类型或枚举值，导致潜在的运行时错误和程序崩溃。

**After**:

结合 `sealed` 类和模式匹配的穷尽性检查，确保所有情况都被覆盖，大幅减少运行时错误和调试时间。

| Metric | Before | After | Change |
|---|---|---|---|
| 运行时错误率 | 10% | 1% | -90% |

## Readme

# 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:**
```dart
var data = {
  'user': ['Lily', 13],
};
```

**Implementation:**
```dart
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:**
```dart
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:**
```dart
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:**
```dart
switch (shape) {
  case Square(size: var s) || Circle(size: var s) when s > 0:
    print('Valid symmetric shape with size $s');
  case Square() || Circle():
    print('Invalid or empty shape');
  default:
    print('Unknown shape');
}
```


---
*Source: https://skills.yangsir.net/skill/gh-dart-use-pattern-matching*
*Markdown mirror: https://skills.yangsir.net/api/skill/gh-dart-use-pattern-matching/markdown*