reis-mobile 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +24 -0
- package/.claude-plugin/plugin.json +12 -0
- package/CHANGELOG.md +103 -0
- package/LICENSE +21 -0
- package/README.md +631 -0
- package/THIRD_PARTY_NOTICES.md +105 -0
- package/agents/flutter-architect.md +92 -0
- package/agents/flutter-performance-engineer.md +103 -0
- package/agents/flutter-test-engineer.md +100 -0
- package/agents/lead-mobile.md +107 -0
- package/agents/mobile-code-reviewer.md +66 -0
- package/agents/mobile-staff-engineer.md +85 -0
- package/agents/plugin-native-expert.md +97 -0
- package/bin/reis-mobile.mjs +256 -0
- package/commands/debate.md +135 -0
- package/commands/doctor.md +23 -0
- package/commands/reis-mobile.md +50 -0
- package/commands/review.md +53 -0
- package/core/config/language.mjs +83 -0
- package/core/context/context-engine.mjs +113 -0
- package/core/detection/project-probe.mjs +104 -0
- package/core/detection/stack-detector.mjs +204 -0
- package/core/diagnostics/doctor.mjs +181 -0
- package/core/install/claude-plugin.mjs +76 -0
- package/core/paths.mjs +4 -0
- package/core/registry/frontmatter.mjs +81 -0
- package/core/registry/registry.mjs +140 -0
- package/core/router/intent-detector.mjs +75 -0
- package/core/router/intents.mjs +91 -0
- package/core/router/router.mjs +83 -0
- package/core/security/redact.mjs +24 -0
- package/package.json +35 -0
- package/skills/architecture-feature-first/SKILL.md +116 -0
- package/skills/bloc/SKILL.md +354 -0
- package/skills/code-review/SKILL.md +89 -0
- package/skills/dart-3-updates/SKILL.md +223 -0
- package/skills/dart-add-unit-test/SKILL.md +126 -0
- package/skills/dart-collect-coverage/SKILL.md +145 -0
- package/skills/dart-fix-runtime-errors/SKILL.md +170 -0
- package/skills/dart-generate-test-mocks/SKILL.md +159 -0
- package/skills/dart-migrate-to-checks-package/SKILL.md +532 -0
- package/skills/dart-resolve-package-conflicts/SKILL.md +120 -0
- package/skills/dart-run-static-analysis/SKILL.md +108 -0
- package/skills/dart-use-pattern-matching/SKILL.md +240 -0
- package/skills/dart-use-pattern-matching/examples/json_patterns.dart +85 -0
- package/skills/detect-mobile-stack/SKILL.md +33 -0
- package/skills/effective-dart/SKILL.md +187 -0
- package/skills/firebase-ai/SKILL.md +93 -0
- package/skills/firebase-analytics/SKILL.md +129 -0
- package/skills/firebase-app-check/SKILL.md +124 -0
- package/skills/firebase-auth/SKILL.md +224 -0
- package/skills/firebase-cloud-firestore/SKILL.md +165 -0
- package/skills/firebase-cloud-functions/SKILL.md +148 -0
- package/skills/firebase-crashlytics/SKILL.md +182 -0
- package/skills/firebase-data-connect/SKILL.md +80 -0
- package/skills/firebase-database/SKILL.md +224 -0
- package/skills/firebase-in-app-messaging/SKILL.md +111 -0
- package/skills/firebase-messaging/SKILL.md +201 -0
- package/skills/firebase-remote-config/SKILL.md +131 -0
- package/skills/firebase-storage/SKILL.md +158 -0
- package/skills/flutter-add-integration-test/SKILL.md +167 -0
- package/skills/flutter-add-widget-preview/SKILL.md +149 -0
- package/skills/flutter-add-widget-test/SKILL.md +158 -0
- package/skills/flutter-adding-home-screen-widgets/SKILL.md +205 -0
- package/skills/flutter-animating-apps/SKILL.md +185 -0
- package/skills/flutter-app-architecture/SKILL.md +80 -0
- package/skills/flutter-apply-architecture-best-practices/SKILL.md +166 -0
- package/skills/flutter-build-responsive-layout/SKILL.md +143 -0
- package/skills/flutter-building-forms/SKILL.md +125 -0
- package/skills/flutter-building-layouts/SKILL.md +131 -0
- package/skills/flutter-building-plugins/SKILL.md +209 -0
- package/skills/flutter-caching-data/SKILL.md +171 -0
- package/skills/flutter-change-notifier/SKILL.md +138 -0
- package/skills/flutter-embedding-native-views/SKILL.md +200 -0
- package/skills/flutter-errors/SKILL.md +126 -0
- package/skills/flutter-fix-layout-issues/SKILL.md +134 -0
- package/skills/flutter-handling-concurrency/SKILL.md +182 -0
- package/skills/flutter-implement-json-serialization/SKILL.md +157 -0
- package/skills/flutter-improving-accessibility/SKILL.md +113 -0
- package/skills/flutter-interoperating-with-native-apis/SKILL.md +130 -0
- package/skills/flutter-login-usecase/SKILL.md +38 -0
- package/skills/flutter-managing-state/SKILL.md +206 -0
- package/skills/flutter-project-audit/SKILL.md +66 -0
- package/skills/flutter-reducing-app-size/SKILL.md +103 -0
- package/skills/flutter-secure-token-store/SKILL.md +35 -0
- package/skills/flutter-setting-up-on-macos/SKILL.md +78 -0
- package/skills/flutter-setup-declarative-routing/SKILL.md +259 -0
- package/skills/flutter-setup-localization/SKILL.md +214 -0
- package/skills/flutter-theming-apps/SKILL.md +163 -0
- package/skills/flutter-use-http-package/SKILL.md +178 -0
- package/skills/flutter-widget-review/SKILL.md +54 -0
- package/skills/flutter-working-with-databases/SKILL.md +203 -0
- package/skills/flutterfire-configure/SKILL.md +143 -0
- package/skills/mobile-security-audit/SKILL.md +101 -0
- package/skills/mockito/SKILL.md +149 -0
- package/skills/mocktail/SKILL.md +122 -0
- package/skills/patrol-e2e-testing/SKILL.md +224 -0
- package/skills/provider/SKILL.md +139 -0
- package/skills/riverpod/SKILL.md +246 -0
- package/skills/testing/SKILL.md +50 -0
- package/stacks/android/stack.json +8 -0
- package/stacks/flutter/stack.json +8 -0
- package/stacks/ios/stack.json +8 -0
- package/stacks/kotlin-multiplatform/stack.json +8 -0
- package/stacks/react-native/stack.json +8 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dart-resolve-package-conflicts
|
|
3
|
+
description: Workflow for fixing package version conflicts. Use this when `pub get` fails due to incompatible package versions.
|
|
4
|
+
metadata:
|
|
5
|
+
model: models/gemini-3.1-pro-preview
|
|
6
|
+
last_modified: Fri, 24 Apr 2026 15:11:14 GMT
|
|
7
|
+
intents: [debug, dependency]
|
|
8
|
+
stacks: [flutter]
|
|
9
|
+
source: https://github.com/dart-lang/skills
|
|
10
|
+
license: BSD-3-Clause
|
|
11
|
+
---
|
|
12
|
+
# Managing Dart Dependencies
|
|
13
|
+
|
|
14
|
+
## Contents
|
|
15
|
+
- [Core Concepts](#core-concepts)
|
|
16
|
+
- [Version Constraints](#version-constraints)
|
|
17
|
+
- [Workflow: Auditing Dependencies](#workflow-auditing-dependencies)
|
|
18
|
+
- [Workflow: Upgrading Dependencies](#workflow-upgrading-dependencies)
|
|
19
|
+
- [Workflow: Resolving Version Conflicts](#workflow-resolving-version-conflicts)
|
|
20
|
+
- [Examples](#examples)
|
|
21
|
+
|
|
22
|
+
## Core Concepts
|
|
23
|
+
|
|
24
|
+
Dart enforces a strict single-version rule for dependencies: a project and all its transitive dependencies must resolve to a single, shared version of any given package. This prevents runtime type mismatches but introduces the risk of "version lock."
|
|
25
|
+
|
|
26
|
+
To mitigate version lock, Dart relies on version constraints rather than pinned versions in the `pubspec.yaml`. The `pubspec.lock` file maintains the exact resolved versions for reproducible builds.
|
|
27
|
+
|
|
28
|
+
Understand the output columns of `dart pub outdated`:
|
|
29
|
+
* **Current:** The version currently recorded in `pubspec.lock`.
|
|
30
|
+
* **Upgradable:** The latest version allowed by the constraints in `pubspec.yaml`. `dart pub upgrade` resolves to this.
|
|
31
|
+
* **Resolvable:** The absolute latest version that can be resolved when factoring in all other dependencies in the project.
|
|
32
|
+
* **Latest:** The latest published version of the package (excluding prereleases).
|
|
33
|
+
|
|
34
|
+
## Version Constraints
|
|
35
|
+
|
|
36
|
+
* **Use Caret Syntax:** Always use caret syntax (e.g., `^1.2.3`) for dependencies in `pubspec.yaml`. This allows `pub` to select newer, non-breaking versions (up to, but not including, the next major version) during resolution.
|
|
37
|
+
* **Tighten Dev Dependencies:** Set the lower bound of `dev_dependencies` to the exact version currently used. This reduces resolution complexity and prevents older, incompatible dev tools from being selected.
|
|
38
|
+
* **Enforce Lockfiles in CI:** Use `dart pub get --enforce-lockfile` in CI/CD pipelines to ensure the exact versions tested locally are used in production.
|
|
39
|
+
|
|
40
|
+
## Workflow: Auditing Dependencies
|
|
41
|
+
|
|
42
|
+
Run this workflow periodically to identify stale packages that may impact stability or performance.
|
|
43
|
+
|
|
44
|
+
**Task Progress:**
|
|
45
|
+
- [ ] Run `dart pub outdated`.
|
|
46
|
+
- [ ] Review the **Upgradable** column to identify packages that can be updated without modifying `pubspec.yaml`.
|
|
47
|
+
- [ ] Review the **Resolvable** column to identify packages that require constraint modifications in `pubspec.yaml` to update.
|
|
48
|
+
- [ ] Identify any packages marked as retracted or discontinued.
|
|
49
|
+
|
|
50
|
+
## Workflow: Upgrading Dependencies
|
|
51
|
+
|
|
52
|
+
Use conditional logic based on the audit results to upgrade dependencies.
|
|
53
|
+
|
|
54
|
+
**Task Progress:**
|
|
55
|
+
- [ ] **If updating to "Upgradable" versions:**
|
|
56
|
+
- [ ] Run `dart pub upgrade`.
|
|
57
|
+
- [ ] Run `dart pub upgrade --tighten` to automatically update the lower bounds in `pubspec.yaml` to match the newly resolved versions.
|
|
58
|
+
- [ ] **If updating to "Resolvable" versions (Major updates):**
|
|
59
|
+
- [ ] Manually edit `pubspec.yaml` to bump the version constraint to match the "Resolvable" column (e.g., change `^0.11.0` to `^0.12.1`).
|
|
60
|
+
- [ ] Run `dart pub upgrade` to resolve the new constraints and update `pubspec.lock`.
|
|
61
|
+
- [ ] **Feedback Loop:**
|
|
62
|
+
- [ ] Run `dart analyze` -> review errors -> fix breaking API changes.
|
|
63
|
+
- [ ] Run `dart test` -> review failures -> fix regressions.
|
|
64
|
+
|
|
65
|
+
## Workflow: Resolving Version Conflicts
|
|
66
|
+
|
|
67
|
+
When `pub` cannot find a set of concrete versions that satisfy all constraints, or when dealing with a retracted package version, manipulate the lockfile surgically.
|
|
68
|
+
|
|
69
|
+
**NEVER** delete the entire `pubspec.lock` file and run `dart pub get`. This causes uncontrolled upgrades across the entire dependency graph.
|
|
70
|
+
|
|
71
|
+
**Task Progress:**
|
|
72
|
+
- [ ] Open `pubspec.lock`.
|
|
73
|
+
- [ ] Locate the specific YAML block for the conflicting or retracted package.
|
|
74
|
+
- [ ] Delete ONLY that package's entry from the lockfile.
|
|
75
|
+
- [ ] Run `dart pub get` to fetch the newest compatible, non-retracted version for that specific package.
|
|
76
|
+
- [ ] **Feedback Loop:**
|
|
77
|
+
- [ ] Run `dart pub deps` -> verify the dependency graph resolves correctly.
|
|
78
|
+
- [ ] If resolution fails, identify the transitive dependency causing the lock, update its constraint in `pubspec.yaml`, and retry.
|
|
79
|
+
|
|
80
|
+
## Examples
|
|
81
|
+
|
|
82
|
+
### Tightening Constraints
|
|
83
|
+
When `dart pub outdated` shows a package is resolvable to a higher minor/patch version, use the `--tighten` flag to update the `pubspec.yaml` automatically.
|
|
84
|
+
|
|
85
|
+
**Input (`pubspec.yaml`):**
|
|
86
|
+
```yaml
|
|
87
|
+
dependencies:
|
|
88
|
+
http: ^0.13.0
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Command:**
|
|
92
|
+
```bash
|
|
93
|
+
dart pub upgrade --tighten http
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Output (`pubspec.yaml`):**
|
|
97
|
+
```yaml
|
|
98
|
+
dependencies:
|
|
99
|
+
http: ^0.13.5
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Surgical Lockfile Removal
|
|
103
|
+
If `package_a` is retracted or locked in a conflict, remove only its block from `pubspec.lock`.
|
|
104
|
+
|
|
105
|
+
**Before (`pubspec.lock`):**
|
|
106
|
+
```yaml
|
|
107
|
+
packages:
|
|
108
|
+
package_a:
|
|
109
|
+
dependency: "direct main"
|
|
110
|
+
description:
|
|
111
|
+
name: package_a
|
|
112
|
+
url: "https://pub.dev"
|
|
113
|
+
source: hosted
|
|
114
|
+
version: "1.0.0" # Retracted version
|
|
115
|
+
package_b:
|
|
116
|
+
dependency: "direct main"
|
|
117
|
+
# ...
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Action:** Delete the `package_a` block entirely. Leave `package_b` untouched. Run `dart pub get`.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dart-run-static-analysis
|
|
3
|
+
description: Execute `dart analyze` to identify warnings and errors, and use `dart fix --apply` to automatically resolve mechanical lint issues. Use during development to ensure code quality and before committing changes.
|
|
4
|
+
metadata:
|
|
5
|
+
model: models/gemini-3.1-pro-preview
|
|
6
|
+
last_modified: Fri, 24 Apr 2026 15:09:34 GMT
|
|
7
|
+
intents: [review]
|
|
8
|
+
stacks: [flutter]
|
|
9
|
+
source: https://github.com/dart-lang/skills
|
|
10
|
+
license: BSD-3-Clause
|
|
11
|
+
---
|
|
12
|
+
# Analyzing and Fixing Dart Code
|
|
13
|
+
|
|
14
|
+
## Contents
|
|
15
|
+
- [Analysis Configuration](#analysis-configuration)
|
|
16
|
+
- [Diagnostic Suppression](#diagnostic-suppression)
|
|
17
|
+
- [Workflow: Executing Static Analysis](#workflow-executing-static-analysis)
|
|
18
|
+
- [Workflow: Applying Automated Fixes](#workflow-applying-automated-fixes)
|
|
19
|
+
- [Examples](#examples)
|
|
20
|
+
|
|
21
|
+
## Analysis Configuration
|
|
22
|
+
|
|
23
|
+
Configure the Dart analyzer using the `analysis_options.yaml` file located at the package root.
|
|
24
|
+
|
|
25
|
+
- **Base Configuration:** Always include a standard rule set (e.g., `package:lints/recommended.yaml` or `package:flutter_lints/flutter.yaml`) using the `include:` directive.
|
|
26
|
+
- **Strict Type Checks:** Enable strict type checks under the `analyzer: language:` node to prevent implicit downcasts and dynamic inferences. Set `strict-casts: true`, `strict-inference: true`, and `strict-raw-types: true`.
|
|
27
|
+
- **Linter Rules:** Explicitly enable or disable specific rules under the `linter: rules:` node. Use a key-value map (`rule_name: true/false`) when overriding included rules, or a list (`- rule_name`) when defining a fresh set. Do not mix list and map syntax in the same `rules` block.
|
|
28
|
+
- **Formatter Configuration:** Configure `dart format` behavior under the `formatter:` node. Set `page_width` (default 80) and `trailing_commas` (`automate` or `preserve`).
|
|
29
|
+
- **Analyzer Plugins:** Enable custom diagnostics by adding plugins under the `analyzer: plugins:` node. Ensure the plugin package is added as a `dev_dependency` in `pubspec.yaml`.
|
|
30
|
+
|
|
31
|
+
## Diagnostic Suppression
|
|
32
|
+
|
|
33
|
+
When a diagnostic (lint or warning) yields a false positive or applies to generated code, suppress it explicitly.
|
|
34
|
+
|
|
35
|
+
- **File-level Exclusion:** Use the `analyzer: exclude:` node in `analysis_options.yaml` to exclude entire files or directories (e.g., `**/*.g.dart`) using glob patterns.
|
|
36
|
+
- **File-level Suppression:** Add `// ignore_for_file: <diagnostic_code>` at the top of a Dart file to suppress specific diagnostics for the entire file. Use `// ignore_for_file: type=lint` to suppress all linter rules.
|
|
37
|
+
- **Line-level Suppression:** Add `// ignore: <diagnostic_code>` on the line directly above the offending code, or appended to the end of the offending line.
|
|
38
|
+
- **Pubspec Suppression:** Add `# ignore: <diagnostic_code>` above the offending line in `pubspec.yaml` files (e.g., `# ignore: sort_pub_dependencies`).
|
|
39
|
+
- **Plugin Diagnostics:** Prefix the diagnostic code with the plugin name when suppressing plugin-specific issues (e.g., `// ignore: some_plugin/some_code`).
|
|
40
|
+
|
|
41
|
+
## Workflow: Executing Static Analysis
|
|
42
|
+
|
|
43
|
+
Use this workflow to identify type-related bugs, style violations, and potential runtime errors.
|
|
44
|
+
|
|
45
|
+
**Task Progress:**
|
|
46
|
+
- [ ] 1. Verify `analysis_options.yaml` exists at the project root.
|
|
47
|
+
- [ ] 2. Run the analyzer using the `analyze_files` MCP tool (if available) or the CLI command `dart analyze <target_directory>`.
|
|
48
|
+
- [ ] 3. Review the diagnostic output.
|
|
49
|
+
- [ ] 4. If info-level issues must be treated as failures, append the `--fatal-infos` flag.
|
|
50
|
+
- [ ] 5. Resolve reported errors manually or proceed to the Automated Fixes workflow.
|
|
51
|
+
|
|
52
|
+
## Workflow: Applying Automated Fixes
|
|
53
|
+
|
|
54
|
+
Use this workflow to resolve outdated API usages, apply quick fixes, and migrate code (e.g., Dart 3 migrations).
|
|
55
|
+
|
|
56
|
+
**Task Progress:**
|
|
57
|
+
- [ ] 1. Execute a dry run to preview proposed changes using the `dart_fix` MCP tool or CLI command `dart fix --dry-run`.
|
|
58
|
+
- [ ] 2. Review the proposed fixes to ensure they align with the intended architecture.
|
|
59
|
+
- [ ] 3. If additional fixes are required, verify that the corresponding linter rules are enabled in `analysis_options.yaml`.
|
|
60
|
+
- [ ] 4. Apply the fixes using the `dart_fix` MCP tool or CLI command `dart fix --apply`.
|
|
61
|
+
- [ ] 5. Format the modified code using the `dart_format` MCP tool or CLI command `dart format .`.
|
|
62
|
+
- [ ] 6. Run the static analysis workflow to verify all diagnostics are resolved.
|
|
63
|
+
|
|
64
|
+
## Examples
|
|
65
|
+
|
|
66
|
+
### Comprehensive `analysis_options.yaml`
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
include: package:flutter_lints/recommended.yaml
|
|
70
|
+
|
|
71
|
+
analyzer:
|
|
72
|
+
exclude:
|
|
73
|
+
- "**/*.g.dart"
|
|
74
|
+
- "lib/generated/**"
|
|
75
|
+
language:
|
|
76
|
+
strict-casts: true
|
|
77
|
+
strict-inference: true
|
|
78
|
+
strict-raw-types: true
|
|
79
|
+
errors:
|
|
80
|
+
todo: ignore
|
|
81
|
+
invalid_assignment: warning
|
|
82
|
+
missing_return: error
|
|
83
|
+
|
|
84
|
+
linter:
|
|
85
|
+
rules:
|
|
86
|
+
avoid_shadowing_type_parameters: false
|
|
87
|
+
await_only_futures: true
|
|
88
|
+
use_super_parameters: true
|
|
89
|
+
|
|
90
|
+
formatter:
|
|
91
|
+
page_width: 100
|
|
92
|
+
trailing_commas: preserve
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Inline Diagnostic Suppression
|
|
96
|
+
|
|
97
|
+
```dart
|
|
98
|
+
// Suppress for the entire file
|
|
99
|
+
// ignore_for_file: unused_local_variable, dead_code
|
|
100
|
+
|
|
101
|
+
void processData() {
|
|
102
|
+
// Suppress for a specific line
|
|
103
|
+
// ignore: invalid_assignment
|
|
104
|
+
int x = '';
|
|
105
|
+
|
|
106
|
+
const y = 10; // ignore: constant_identifier_names
|
|
107
|
+
}
|
|
108
|
+
```
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dart-use-pattern-matching
|
|
3
|
+
description: >-
|
|
4
|
+
Applies Dart 3 pattern matching, switch expressions, and destructuring
|
|
5
|
+
idiomatically to validate data schemas, handle algebraic data types, and
|
|
6
|
+
decompose control flow. Use when refactoring complex if-else chains,
|
|
7
|
+
parsing polymorphic JSON or API responses, destructuring Records or Maps, or
|
|
8
|
+
enforcing exhaustiveness on sealed classes. Don't use for simple boolean
|
|
9
|
+
conditions, single-variable type promotion (use `is`), or basic collection
|
|
10
|
+
filtering.
|
|
11
|
+
metadata:
|
|
12
|
+
model: models/gemini-3.1-pro-preview
|
|
13
|
+
last_modified: Sun, 06 Sep 2026 06:43:00 GMT
|
|
14
|
+
routing: manual
|
|
15
|
+
stacks: [flutter]
|
|
16
|
+
source: https://github.com/dart-lang/skills
|
|
17
|
+
license: BSD-3-Clause
|
|
18
|
+
---
|
|
19
|
+
# Implementing Dart Patterns
|
|
20
|
+
|
|
21
|
+
## Contents
|
|
22
|
+
- [Pattern Selection Strategy](#pattern-selection-strategy)
|
|
23
|
+
- [Switch Statements vs. Expressions](#switch-statements-vs-expressions)
|
|
24
|
+
- [Core Pattern Implementations](#core-pattern-implementations)
|
|
25
|
+
- [Pragmatic Balance & Anti-Patterns](#pragmatic-balance--anti-patterns)
|
|
26
|
+
- [Workflows](#workflows)
|
|
27
|
+
- [Examples](#examples)
|
|
28
|
+
|
|
29
|
+
## Pattern Selection Strategy
|
|
30
|
+
|
|
31
|
+
Apply specific pattern types based on the data structure and desired outcome. Follow these conditional guidelines:
|
|
32
|
+
|
|
33
|
+
* **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.
|
|
34
|
+
* **If handling polymorphic payloads or responses:** Use `switch` expressions over map discriminant keys to deserialize into `sealed` class hierarchies.
|
|
35
|
+
* **If handling multiple return values:** Use Record patterns to destructure fields directly into local variables.
|
|
36
|
+
* **If executing type-specific behavior (Algebraic Data Types):** Use Object patterns combined with `sealed` classes to ensure exhaustiveness.
|
|
37
|
+
* **If matching numeric ranges or conditions:** Use Relational (`>=`, `<=`) and Logical-and (`&&`) patterns within switch arms.
|
|
38
|
+
* **If multiple cases share logic:** Use Logical-or (`||`) patterns to share a single case body or guard clause.
|
|
39
|
+
* **If ignoring specific values:** Use the Wildcard pattern (`_`) or a non-matching Rest element (`...`) in collections.
|
|
40
|
+
|
|
41
|
+
## Switch Statements vs. Expressions
|
|
42
|
+
|
|
43
|
+
Select the appropriate switch construct based on the execution context:
|
|
44
|
+
|
|
45
|
+
* **If producing a value:** Use a **switch expression**.
|
|
46
|
+
* Syntax: `switch (value) { pattern => expression, }`
|
|
47
|
+
* Rule: Each case must be a single expression. No implicit fallthrough. Must be exhaustive.
|
|
48
|
+
* **If executing statements or side effects:** Use a **switch statement**.
|
|
49
|
+
* Syntax: `switch (value) { case pattern: statements; }`
|
|
50
|
+
* Rule: Empty cases fall through to the next case. Non-empty cases implicitly break (no `break` keyword required).
|
|
51
|
+
|
|
52
|
+
## Core Pattern Implementations
|
|
53
|
+
|
|
54
|
+
Implement patterns using the following syntax and rules:
|
|
55
|
+
|
|
56
|
+
* **Logical-or (`||`):** `pattern1 || pattern2`. Both branches must define the exact same set of variables.
|
|
57
|
+
* **Logical-and (`&&`):** `pattern1 && pattern2`. Branches must *not* define overlapping variables.
|
|
58
|
+
* **Relational:** `==`, `!=`, `<`, `>`, `<=`, `>=` followed by a constant expression.
|
|
59
|
+
* **Cast (`as`):** `pattern as Type`. Throws if the value does not match the type. Use to forcibly assert types during destructuring.
|
|
60
|
+
* **Null-check (`?`):** `pattern?`. Fails the match if the value is null. Binds the variable to the non-nullable base type.
|
|
61
|
+
* **Null-assert (`!`):** `pattern!`. Throws if the value is null.
|
|
62
|
+
* **Variable:** `var name` or `Type name`. Binds the matched value to a new local variable.
|
|
63
|
+
* **Wildcard (`_`):** Matches any value and discards it.
|
|
64
|
+
* **List:** `[pattern1, pattern2]`. Matches lists of exact length unless a Rest element (`...` or `...var rest`) is used.
|
|
65
|
+
* **Map:** `{"key": pattern}`. Matches maps containing the specified keys. Ignores unmatched keys.
|
|
66
|
+
* **Record:** `(pattern1, named: pattern2)`. Matches records of the exact shape. Use `:var name` to infer the getter name.
|
|
67
|
+
* **Object:** `ClassName(field: pattern)`. Matches instances of `ClassName`. Use `:var field` to infer the getter name.
|
|
68
|
+
|
|
69
|
+
## Pragmatic Balance & Anti-Patterns
|
|
70
|
+
|
|
71
|
+
Pattern matching and switch expressions should simplify code, not add syntactic overhead. Observe the following boundaries:
|
|
72
|
+
|
|
73
|
+
### 1. Prefer `is` Type Promotion over `if-case` for Single Promotable Variables
|
|
74
|
+
When checking or promoting a single variable, use standard `is` checks instead of `if-case` patterns that introduce shadow aliases.
|
|
75
|
+
|
|
76
|
+
* **Prefer:**
|
|
77
|
+
```dart
|
|
78
|
+
// ✅ Promotes `key` directly in-place without extra variables
|
|
79
|
+
for (final MapEntry(:key, :value) in map.entries) {
|
|
80
|
+
if (key is String && value != null) {
|
|
81
|
+
process(key, value);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
* **Avoid:**
|
|
86
|
+
```dart
|
|
87
|
+
// ❌ Anti-pattern: Introduces unnecessary alias variable `k`
|
|
88
|
+
for (final MapEntry(:key, :value) in map.entries) {
|
|
89
|
+
if (key case final String k when value != null) {
|
|
90
|
+
process(k, value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 2. Consolidate Nullable Types in Switch Arms
|
|
96
|
+
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.
|
|
97
|
+
|
|
98
|
+
* **Prefer:**
|
|
99
|
+
```dart
|
|
100
|
+
// ✅ Clean nullable pattern match
|
|
101
|
+
switch (value) {
|
|
102
|
+
final String? s => s,
|
|
103
|
+
_ => throw FormatException('Invalid value: $value'),
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
* **Avoid:**
|
|
107
|
+
```dart
|
|
108
|
+
// ❌ Redundant separate null arm
|
|
109
|
+
switch (value) {
|
|
110
|
+
final String s => s,
|
|
111
|
+
null => null,
|
|
112
|
+
_ => throw FormatException('Invalid value: $value'),
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 3. Preserve Fast-Fail Validation (Do Not Silently Drop Data)
|
|
117
|
+
Do not use `if-case` in loops or deserialization to filter elements if malformed data should trigger an error or diagnostic warning.
|
|
118
|
+
|
|
119
|
+
* **Prefer:**
|
|
120
|
+
```dart
|
|
121
|
+
// ✅ Fast-fail with explicit diagnostic error
|
|
122
|
+
for (final raw in rawTasks) {
|
|
123
|
+
if (raw is! Map<String, dynamic>) {
|
|
124
|
+
throw FormatException('Expected Map item, got ${raw.runtimeType}: $raw');
|
|
125
|
+
}
|
|
126
|
+
_applyTask(raw);
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
* **Avoid:**
|
|
130
|
+
```dart
|
|
131
|
+
// ❌ Silently ignores malformed items
|
|
132
|
+
for (final raw in rawTasks) {
|
|
133
|
+
if (raw case final Map<String, dynamic> taskMap) {
|
|
134
|
+
_applyTask(taskMap);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 4. Avoid Single-Case or Boolean Switches
|
|
140
|
+
* Use `if (x is T)` instead of a `switch` statement with only 1 case and `default: break;`.
|
|
141
|
+
* Use standard conditional ternary operators (`condition ? a : b`) instead of `switch (condition) { true => a, false => b }`.
|
|
142
|
+
|
|
143
|
+
### 5. Avoid Gratuitous Object Destructuring
|
|
144
|
+
Use standard property access (`user.name`) rather than object pattern destructuring (`final User(:name) = user;`) when reading a single property on a known non-null instance.
|
|
145
|
+
|
|
146
|
+
### 6. Avoid `if-case` for Standalone Scalar Comparisons
|
|
147
|
+
Use standard boolean operators (`if (code >= 200 && code < 300)`) instead of `if (code case >= 200 && < 300)` for standalone conditions. Reserve relational patterns for multi-arm `switch` tables.
|
|
148
|
+
|
|
149
|
+
## Workflows
|
|
150
|
+
|
|
151
|
+
### Task Progress: Implementing Pattern Matching
|
|
152
|
+
Copy this checklist to track progress when implementing complex pattern matching logic:
|
|
153
|
+
|
|
154
|
+
- [ ] Identify the data structure being evaluated (JSON, Record, Class, Enum).
|
|
155
|
+
- [ ] Select the appropriate switch construct (Expression for values, Statement for side-effects).
|
|
156
|
+
- [ ] Define the required patterns (Object, Map, List, Record).
|
|
157
|
+
- [ ] Extract required data using Variable patterns (`var x`, `:var y`).
|
|
158
|
+
- [ ] Apply Guard clauses (`when condition`) for logic that cannot be expressed via patterns.
|
|
159
|
+
- [ ] Handle unmatched cases using a Wildcard (`_`) or `default` clause (if not using a sealed class).
|
|
160
|
+
- [ ] Run static analyzer for exhaustiveness and dead code (`dart analyze`).
|
|
161
|
+
- [ ] Verify runtime Map/JSON pattern behavior on omitted keys (`containsKey` semantics) vs explicit `null` values.
|
|
162
|
+
|
|
163
|
+
### Feedback Loop 1: Exhaustiveness Checking (Static Verification)
|
|
164
|
+
When switching over `sealed` classes or enums, ensure all subtypes are handled at compile time:
|
|
165
|
+
|
|
166
|
+
1. **Run analyzer:** Execute `dart analyze`.
|
|
167
|
+
2. **Review errors:** Look for "The type 'X' is not exhaustively matched by the switch cases" or unreachable pattern arm warnings.
|
|
168
|
+
3. **Fix:** Add the missing Object patterns for unhandled subtypes, or add an explicit wildcard (`_`) arm if a default fallback or error is acceptable.
|
|
169
|
+
|
|
170
|
+
### Feedback Loop 2: Runtime Map & JSON Pattern Verification
|
|
171
|
+
Because `dart analyze` cannot statically verify dynamic `Map<String, dynamic>` keys, validate runtime pattern semantics explicitly:
|
|
172
|
+
|
|
173
|
+
1. **Omitted Keys vs. Explicit `null`**: A map pattern `{'key': String? val}` checks `map.containsKey('key')`. If `'key'` is omitted from the JSON payload, the pattern fails to match at runtime even though `String?` is nullable. Extract optional keys from the validated map directly (`map['key'] as String?`).
|
|
174
|
+
2. **Fast-Fail Fallback**: Ensure unmatched or malformed map structures hit an explicit `_ => throw FormatException(...)` arm rather than silently failing an `if-case` check.
|
|
175
|
+
|
|
176
|
+
## Examples
|
|
177
|
+
|
|
178
|
+
### Polymorphic JSON Deserialization (Discriminated Unions)
|
|
179
|
+
Use Map patterns with switch expressions to validate tagged JSON payloads and
|
|
180
|
+
construct `sealed` class hierarchies. See
|
|
181
|
+
[examples/json_patterns.dart](examples/json_patterns.dart) for an executable
|
|
182
|
+
implementation demonstrating tagged `ApiResponse` parsing into `SuccessResponse`
|
|
183
|
+
and `ErrorResponse`.
|
|
184
|
+
|
|
185
|
+
### Nested JSON Validation and Optional Fields
|
|
186
|
+
Use nested Map and List patterns to validate required schema structure and
|
|
187
|
+
extract collections in a single step. See
|
|
188
|
+
[examples/json_patterns.dart](examples/json_patterns.dart) for an executable
|
|
189
|
+
implementation of `processUserPayload`.
|
|
190
|
+
|
|
191
|
+
Map patterns check for key existence (`containsKey`). If an optional JSON key
|
|
192
|
+
might be omitted entirely from the payload (rather than explicitly passed as
|
|
193
|
+
`'key': null`), destructure required keys via the pattern and extract optional
|
|
194
|
+
fields directly from the matched submap.
|
|
195
|
+
|
|
196
|
+
### Algebraic Data Types (Sealed Classes)
|
|
197
|
+
Use Object patterns with switch expressions to handle family types exhaustively.
|
|
198
|
+
|
|
199
|
+
```dart
|
|
200
|
+
sealed class Shape {}
|
|
201
|
+
|
|
202
|
+
class Square implements Shape {
|
|
203
|
+
final double length;
|
|
204
|
+
Square(this.length);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
class Circle implements Shape {
|
|
208
|
+
final double radius;
|
|
209
|
+
Circle(this.radius);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Switch expression guarantees exhaustiveness due to `sealed` modifier.
|
|
213
|
+
double calculateArea(Shape shape) => switch (shape) {
|
|
214
|
+
Square(length: var l) => l * l,
|
|
215
|
+
Circle(:var radius) => math.pi * radius * radius,
|
|
216
|
+
};
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Variable Swapping and Destructuring
|
|
220
|
+
Use variable assignment patterns to swap values or extract record fields without temporary variables.
|
|
221
|
+
|
|
222
|
+
```dart
|
|
223
|
+
var (a, b) = ('left', 'right');
|
|
224
|
+
(b, a) = (a, b); // Swap values
|
|
225
|
+
|
|
226
|
+
// Destructuring a function return
|
|
227
|
+
var (name, age) = getUserInfo();
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Guard Clauses and Logical-or
|
|
231
|
+
Use `when` to evaluate arbitrary conditions after a pattern matches.
|
|
232
|
+
|
|
233
|
+
```dart
|
|
234
|
+
switch (shape) {
|
|
235
|
+
case Square(length: var s) || Circle(radius: var s) when s > 0:
|
|
236
|
+
print('Valid positive shape with dimension $s');
|
|
237
|
+
case Square() || Circle():
|
|
238
|
+
print('Zero or negative dimension shape');
|
|
239
|
+
}
|
|
240
|
+
```
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
|
|
2
|
+
// for details. All rights reserved. Use of this source code is governed by a
|
|
3
|
+
// BSD-style license that can be found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
/// Examples of Dart pattern matching for JSON handling:
|
|
6
|
+
/// - Polymorphic JSON deserialization (discriminated unions)
|
|
7
|
+
/// - Nested JSON validation with optional and omitted keys
|
|
8
|
+
library;
|
|
9
|
+
|
|
10
|
+
/// Represents an API response in a sealed hierarchy.
|
|
11
|
+
sealed class ApiResponse {}
|
|
12
|
+
|
|
13
|
+
/// Successful API response carrying data.
|
|
14
|
+
class SuccessResponse implements ApiResponse {
|
|
15
|
+
final Map<String, dynamic> data;
|
|
16
|
+
SuccessResponse(this.data);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/// Error API response carrying an error message and code.
|
|
20
|
+
class ErrorResponse implements ApiResponse {
|
|
21
|
+
final String message;
|
|
22
|
+
final int code;
|
|
23
|
+
ErrorResponse(this.message, this.code);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// Parses a raw JSON map into an [ApiResponse] subtype.
|
|
27
|
+
ApiResponse parseApiResponse(Map<String, dynamic> json) => switch (json) {
|
|
28
|
+
{'status': 'ok', 'data': Map<String, dynamic> data} => SuccessResponse(data),
|
|
29
|
+
{'status': 'error', 'message': String msg, 'code': int code} => ErrorResponse(
|
|
30
|
+
msg,
|
|
31
|
+
code,
|
|
32
|
+
),
|
|
33
|
+
_ => throw FormatException('Invalid or unrecognized API response: $json'),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/// Validates a nested user payload with both required schema and optional keys.
|
|
37
|
+
void processUserPayload(Map<String, dynamic> json) {
|
|
38
|
+
if (json case {
|
|
39
|
+
'id': String id,
|
|
40
|
+
'profile':
|
|
41
|
+
{'name': String name, 'email': String email} &&
|
|
42
|
+
final Map<String, dynamic> profile,
|
|
43
|
+
'tags': [
|
|
44
|
+
String primaryTag,
|
|
45
|
+
...,
|
|
46
|
+
], // Matches at least 1 element, ignores rest
|
|
47
|
+
}) {
|
|
48
|
+
// Read optional/omitted fields directly from the validated submap.
|
|
49
|
+
// Map patterns check for key existence (containsKey), so an omitted key
|
|
50
|
+
// fails matching even if typed as String?.
|
|
51
|
+
final avatarUrl = profile['avatarUrl'] as String?;
|
|
52
|
+
print(
|
|
53
|
+
'User $name ($id, $email, avatar: $avatarUrl) - Primary tag: $primaryTag',
|
|
54
|
+
);
|
|
55
|
+
} else {
|
|
56
|
+
throw FormatException('Malformed user payload structure: $json');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
void main() {
|
|
61
|
+
// Test polymorphic deserialization
|
|
62
|
+
final success = parseApiResponse({
|
|
63
|
+
'status': 'ok',
|
|
64
|
+
'data': {'userId': '123'},
|
|
65
|
+
});
|
|
66
|
+
print('Parsed success: $success');
|
|
67
|
+
|
|
68
|
+
final error = parseApiResponse({
|
|
69
|
+
'status': 'error',
|
|
70
|
+
'message': 'Not found',
|
|
71
|
+
'code': 404,
|
|
72
|
+
});
|
|
73
|
+
print('Parsed error: $error');
|
|
74
|
+
|
|
75
|
+
// Test nested validation
|
|
76
|
+
processUserPayload({
|
|
77
|
+
'id': 'u100',
|
|
78
|
+
'profile': {
|
|
79
|
+
'name': 'Alice',
|
|
80
|
+
'email': 'alice@example.com',
|
|
81
|
+
'avatarUrl': 'https://example.com/avatar.png',
|
|
82
|
+
},
|
|
83
|
+
'tags': ['admin', 'staff'],
|
|
84
|
+
});
|
|
85
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: detect-mobile-stack
|
|
3
|
+
description: Detects the mobile stack of the current project (Flutter, Android, iOS, React Native, Kotlin Multiplatform), its languages and target platforms, using the reis-mobile detector. Use when you need to know which mobile stack a project uses before reviewing, debugging or changing it, or when the user asks "what stack is this project?".
|
|
4
|
+
routing: manual
|
|
5
|
+
stacks: ["*"]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Detect Mobile Stack
|
|
9
|
+
|
|
10
|
+
Use the reis-mobile deterministic detector instead of guessing from the folder structure.
|
|
11
|
+
|
|
12
|
+
## How to run
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
node "${CLAUDE_PLUGIN_ROOT}/bin/reis-mobile.mjs" detect --json --dir "<project directory>"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Result fields:
|
|
19
|
+
|
|
20
|
+
| Field | Meaning |
|
|
21
|
+
|---|---|
|
|
22
|
+
| `stack` | `flutter`, `react-native`, `kotlin-multiplatform`, `android`, `ios` or `unknown` |
|
|
23
|
+
| `variant` | `plugin` (Flutter), `expo` (React Native) or `null` |
|
|
24
|
+
| `languages` | Languages found, the main one first (includes native code in cross-platform apps) |
|
|
25
|
+
| `platforms` | Target platforms (`android`, `ios`, `web`, `macos`, `desktop`...) |
|
|
26
|
+
| `evidence` | Files that justify the detection |
|
|
27
|
+
| `candidates` | Every stack that matched, in priority order |
|
|
28
|
+
|
|
29
|
+
## Interpretation
|
|
30
|
+
|
|
31
|
+
- Cross-platform stacks take priority: a Flutter app contains `android/` and `ios/`, but the stack is `flutter`.
|
|
32
|
+
- `unknown` means there is no mobile project at the given root. In monorepos, run it again with `--dir` pointing at the app.
|
|
33
|
+
- More than one item in `candidates` indicates a hybrid project (for example, KMP with an Android module). Consider both stacks when loading skills.
|