Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "dart-flutter",
"displayName": "Dart and Flutter",
"version": "1.0.5",
"version": "1.0.6",
"description": "Official Claude plugin for Dart and Flutter that installs Flutter/Dart Skills and Dart MCP server for building natively compiled, visually stunning applications for mobile, web, desktop, and embedded devices from a single codebase",
"author": {
"name": "Dart and Flutter",
Expand Down
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dart-flutter",
"version": "1.0.5",
"version": "1.0.6",
"description": "Official Codex plugin for Dart and Flutter that installs Flutter/Dart Skills and Dart MCP server for building natively compiled, visually stunning applications for mobile, web, desktop, and embedded devices from a single codebase.",
"author": {
"name": "Dart and Flutter",
Expand Down
2 changes: 1 addition & 1 deletion .cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "dart-flutter",
"displayName": "Dart and Flutter",
"version": "1.0.5",
"version": "1.0.6",
"description": "Official Cursor plugin for Dart and Flutter that installs Flutter/Dart Skills and Dart MCP server for building natively compiled, visually stunning applications for mobile, web, desktop, and embedded devices from a single codebase.",
"author": {
"name": "Dart and Flutter Team",
Expand Down
16 changes: 10 additions & 6 deletions skills/dart-use-path-package/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: >-
Cross-platform file and directory path manipulation, segment splitting, extension extraction, and context conversion using `package:path` and `package:file`. Use when writing, inspecting, joining, splitting, or refactoring file paths, directory names, or extensions, or replacing raw string path operations (`.split('/')`, `'$dir/$file'`, `.endsWith('.ext')`, `.replaceAll('\\', '/')`). Don't use for HTTP network URI routing, database query strings, or non-path string processing.
metadata:
model: models/gemini-3.1-pro-preview
last_modified: Sun, 06 Sep 2026 07:14:00 GMT
last_modified: Wed, 23 Sep 2026 22:10:00 GMT
---

# Safe Cross-Platform Path Manipulation in Dart
Expand Down Expand Up @@ -60,10 +60,14 @@ metadata:
false positives on partial substring names (e.g. `barfoo/`).

### Root and Directory Prefixes
* **Prefer**: `p.split(path).first == 'foo'` or `p.isWithin('foo', path)`
* **Avoid**: `path.startsWith('foo/')`
* **Why**: Fails on Windows separators and misses relative prefix variants such
as `./foo/`.
* **Prefer**: `if (p.split(path) case ['foo', ...])` (or `case ['foo', ...final rest]` when extracting tail segments), or `p.isWithin('foo', path)`
* **Avoid**: `path.startsWith('foo/')` or `p.split(path).first == 'foo'`
* **Why**: String prefix matching fails on Windows separators (`foo\bar`). Calling
`p.split(path).first` throws a `StateError` on empty lists and requires
separate `.skip(1)` slicing, whereas list patterns safely check non-emptiness,
match multi-segment prefixes, and optionally bind `...final rest` in a single
step. When unnormalized relative prefixes like `./foo/bar` may appear, use
`p.isWithin('foo', path)` (or `p.split(p.normalize(path))`).
Comment on lines +63 to +70

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When dealing with absolute paths, p.split(path) will include the root directory (such as '/' on POSIX or 'C:\\' on Windows) as the first element in the returned list. Consequently, a pattern match like case ['foo', ...] will fail to match absolute paths even if they start with 'foo' relative to the root. To handle absolute paths safely, consider converting them to relative paths using p.relative(path, from: root) before performing list pattern matching, or use p.isWithin.


### File Extensions
* **Prefer**: `p.extension(path) == '.wasm'`
Expand Down Expand Up @@ -188,7 +192,7 @@ String insertContentHash(String filename, String hash) {

### Path Refactoring Checklist
- [ ] Replace string interpolation (`'$dir/$file'`) with `p.join(dir, file)`.
- [ ] Replace `.contains('dir/')` and `.startsWith('dir/')` with `p.split(path)` segment checks or `p.isWithin(parent, child)`.
- [ ] Replace `.contains('dir/')` and `.startsWith('dir/')` with `p.split(path)` list pattern checks (`case ['dir', ...final rest]`) or `p.isWithin(parent, child)`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The checklist item suggests replacing both .contains('dir/') and .startsWith('dir/') with case ['dir', ...final rest]. However, case ['dir', ...final rest] only matches when 'dir' is the first segment (equivalent to startsWith). To match .contains('dir/'), a pattern like case [..., 'dir', ...] or p.split(path).contains('dir') should be used instead.

- [ ] Replace `.replaceAll(r'\', '/')` with `p.posix.joinAll(p.split(path))` (or `p.url.joinAll`).
- [ ] Replace `.endsWith('.ext')` on file paths with `p.extension(path) == '.ext'`.
- [ ] Replace manual dot-index slicing with `p.withoutExtension(path)` and `p.extension(path, [level])`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ String toGitPath(String relativeNativePath) =>
p.posix.joinAll(p.split(relativeNativePath));

void main() {
print('Git path: ${toGitPath(r'lib\src\file.dart')}');
print('Asset key: ${computeWebAssetKey('assets/icon.png', '.')}');
final nativeSamplePath = p.join('lib', 'src', 'file.dart');
print('Git path: ${toGitPath(nativeSamplePath)}');
print('Asset key: ${computeWebAssetKey(p.join('assets', 'icon.png'), '.')}');
print('Content hash: ${insertContentHash('main.dart.js.map', 'a1b2c3')}');
}
30 changes: 23 additions & 7 deletions skills/dart-use-pattern-matching/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ 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 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.
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: Sun, 06 Sep 2026 06:43:00 GMT
last_modified: Wed, 23 Sep 2026 22:10:00 GMT
---
# Implementing Dart Patterns

Expand All @@ -27,6 +27,7 @@ metadata:
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 executing type-specific behavior (Algebraic Data Types):** Use Object patterns combined with `sealed` classes to ensure exhaustiveness.
Expand Down Expand Up @@ -147,10 +148,10 @@ Use standard boolean operators (`if (code >= 200 && code < 300)`) instead of `if
### 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).
- [ ] Identify the data structure being evaluated (JSON, List/Segments, 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`).
- [ ] Extract required data using Variable patterns (`var x`, `:var y`, `...final rest`).
- [ ] 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 static analyzer for exhaustiveness and dead code (`dart analyze`).
Expand Down Expand Up @@ -189,6 +190,21 @@ might be omitted entirely from the payload (rather than explicitly passed as
`'key': null`), destructure required keys via the pattern and extract optional
fields directly from the matched submap.

### List and Path Segment Destructuring
Use `if-case` with list rest elements (`...final rest`) to validate prefixes and
extract remaining elements without a 2-arm `_ => null` switch or manual `.first`
and `.skip(1)` indexing.

```dart
String? resolveAllowedAssetSubpath(List<String> segments) {
if (segments case ['assets', ...final rest]
when rest.isNotEmpty && !rest.contains('..')) {
return rest.join('/');
}
return null;
}
Comment on lines +199 to +205

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While this is an illustrative example of list pattern matching, using rest.join('/') on unnormalized segments can preserve empty segments (e.g., ['', 'foo'] joining to '/foo') or current directory segments ('.'), which might bypass intended path restrictions or produce malformed paths. For robust path resolution, consider normalizing the path using p.normalize or joining via p.posix.joinAll(rest).

```

### Algebraic Data Types (Sealed Classes)
Use Object patterns with switch expressions to handle family types exhaustively.

Expand Down
2 changes: 1 addition & 1 deletion tool/.dart_skills_githash
Original file line number Diff line number Diff line change
@@ -1 +1 @@
26b2dcc5654cbbc3b2ec56ea94719469bc8bae9e
82a49602fe18cecfe41859592af600cb9d875e40
Loading