Skip to main content
skill Claude Code CX Codex Cursor GitHub Copilot

dart-use-path-package — Claude Code skill: cross-platform Dart paths

Teaches the agent to handle Dart file paths via package:path, avoiding string operations that break on Windows.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add flutter/agent-plugins --skill dart-use-path-package -a claude-code
Open on GitHub

What dart-use-path-package does

The skill replaces unsafe string operations like '$dir/$file', .split('/'), .endsWith('.ext'), or .replaceAll('\\', '/') with package:path methods: p.join for joining, p.split for segment parsing, p.extension for extensions, p.normalize and p.canonicalize for path normalization. The agent learns cross-platform path handling, POSIX and URL conversion for Git and manifests, compound extension processing like .tar.gz and .js.map, and safe path-to-Uri bridging.

Uses Dart list pattern matching for segment inspection, prevents Windows backslash bugs, duplicate separators, and false substring matches. Not for HTTP routing, database query strings, or non-path string processing. Critical for projects targeting Windows: string path operations silently fail where macOS and Linux let them through.

What it helps with

  • join paths without duplicate slashes or Windows backslash bugs
  • extract file extensions correctly without matching directories
  • convert native paths to POSIX format for Git or archives
  • match path segments cross-platform without breaking on backslashes
  • parse compound extensions like .tar.gz or .js.map

Example prompts for the agent

  • Replace all `'$dir/$file'` with `p.join` in lib/
  • Audit path operations in src/ for Windows compatibility
  • Convert paths in manifest.yaml to POSIX format using `p.posix.joinAll`

How to install dart-use-path-package

1Skills CLI (Vercel)

npx skills add flutter/agent-plugins --skill dart-use-path-package -a claude-code

2Manually: put the files in place

~/.claude/skills/dart-use-path-package/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/dart-use-path-package/

From the same set14

The whole set →
flutter-apply-architecture-best-practices flutter This skill structures Flutter apps around UI, Logic, and Data layers. Use it when starting a project or refactoring code for scale. Claude Code CX Codex Cursor +1 43,000installs flutter-build-responsive-layout flutter This skill guides coding agents to build adaptive Flutter UIs across mobile and desktop screens using LayoutBuilder and BoxConstraints. Claude Code CX Codex Cursor +1 42,300installs flutter-add-widget-test flutter A skill that teaches AI coders to write component-level Flutter tests using WidgetTester. Verifies how widgets render and respond to taps, scrolling, and text input. Claude Code CX Codex Cursor +1 39,000installs flutter-add-integration-test flutter Sets up the integration_test package for Flutter and turns MCP session interactions into repeatable end-to-end tests. Claude Code CX Codex Cursor +1 38,000installs flutter-setup-declarative-routing flutter Sets up Flutter routing with go_router, including URL-based screens, nested navigation, and deep links for web and mobile apps. Claude Code CX Codex Cursor +1 37,600installs flutter-implement-json-serialization flutter Teaches coding agents to build Dart models with fromJson and toJson methods using dart:convert. Useful for manual JSON mapping in simple Flutter data structures. Claude Code CX Codex Cursor +1 37,200installs flutter-add-widget-preview flutter Configures interactive Flutter widget previews with package:flutter/widget_previews.dart. Useful for testing UI components in isolation without launching the whole app. Claude Code CX Codex Cursor +1 36,600installs flutter-setup-localization flutter This skill automates initialization of multi-language support in Flutter projects. It adds dependencies, enables code generation, and creates a configuration file for .arb-based translations. Claude Code CX Codex Cursor +1 36,600installs

Similar

write-concept leonardomso Creates and reviews JavaScript concept pages for 33 JavaScript Concepts. Use it when adding, rewriting, or checking material in /docs/concepts/. Claude Code CX Codex Cursor +1 305installs cargo-fuzz trailofbits Sets up cargo-fuzz inside a Cargo-based Rust crate and walks through the harness, sanitizers and crash triage. Claude Code CX Codex Cursor +1 5,400installs lsp-setup official github Installs a language server and configures LSP for Copilot CLI. Use it when you need code navigation such as go-to-definition or find-references. GitHub Copilot 1,100installs swiftdata-pro Paul Hudson A skill for writing, reviewing, and improving SwiftData code with modern APIs and best practices. Essential when working with SwiftData-based projects in Swift. Claude Code CX Codex Cursor +1 9,200installs typescript-advanced-types sickn33 Teaches the agent generics, conditional and mapped types in TypeScript, and stays honest about where type checking stops. Claude Code CX Codex Cursor +1 1,300installs atheris trailofbits Guides setup of coverage-guided fuzzing for Python code and C extensions with Atheris. Use it to investigate crashes and memory corruption. Claude Code CX Codex Cursor +1 5,200installs migrate-to-shoehorn Matt Pocock Replaces as type assertions in tests with helpers from @total-typescript/shoehorn. Use it when tests need partial objects or deliberately invalid data. Claude Code 475,388installs write-swift emilkowalski This skill guides Swift coding, reviews, and migrations using the language’s current practices. Use it to investigate concurrency errors, hangs, data races, or retain cycles. Claude Code CX Codex Cursor +1 117,500installs

Questions about dart-use-path-package

How to install dart-use-path-package in Claude Code?

Run npx skills add flutter/agent-plugins --skill dart-use-path-package -a claude-code. The skill will appear in ~/.claude/skills/dart-use-path-package/.

What does dart-use-path-package do?

Teaches the agent to replace string path operations with package:path methods that work identically on Windows, macOS, and Linux. Prevents backslash bugs, duplicate separators, and false extension matches.

When to use p.normalize vs p.canonicalize?

p.normalize resolves . and .. lexically without touching the filesystem. p.canonicalize is needed to compare physical file identity across symlinks, relative roots, or case-insensitive filesystems.

Source text

SKILL.md · BSD-3-Clause
---
name: dart-use-path-package
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: Wed, 23 Sep 2026 22:10:00 GMT
---

# Safe Cross-Platform Path Manipulation in Dart

## Contents
* [1. Core Principles & Cross-Platform Rules](#1-core-principles--cross-platform-rules)
* [2. Recommended package:path Idioms vs. String Anti-Patterns](#2-recommended-packagepath-idioms-vs-string-anti-patterns)
* [3. Bridging Native Paths to POSIX, Git, & URL Contexts](#3-bridging-native-paths-to-posix-git--url-contexts)
* [4. Mockable File Systems (`package:file` vs. Global `p.*`)](#4-mockable-file-systems-packagefile-vs-global-p)
* [5. Extensions, Compound Extensions & Stem Extraction](#5-extensions-compound-extensions--stem-extraction)
* [6. Workflows & Audit Checklist](#6-workflows--audit-checklist)
* [References & Examples](#references--examples)

---

## 1. Core Principles & Cross-Platform Rules

### Avoid Treating File Paths as Raw Strings
* Native file paths on Windows use backslashes (`\`), whereas macOS and Linux use forward slashes (`/`).
* String operations like `.contains('foo/')`, `.startsWith('foo/')`, or `.split('/')` silently fail on Windows native paths.
* String interpolation like `'$dir/$file'` injects forward slashes on Windows and produces duplicate slashes (`//`) when `$dir` ends with a trailing slash.

**Rule**: Always decompose paths into segments using `p.split(path)` before inspecting directory hierarchy or segment names, and always join path components using `p.join(...)`.

### Pragmatic Boundary Joining vs. Multi-Segment Decomposition (`p.join`)
* **Cross-Platform Libraries (Windows + POSIX)**: Pass individual path segments to `p.join(dir, 'sub', 'file.json')` so `package:path` inserts OS-native separators (`\` on Windows, `/` on POSIX) between every component.
* **POSIX-Only Tools & Static Subpath Greppability**: In codebases exclusively targeting Linux/macOS (or when joining a dynamic base path to a known static subpath), decomposing 5–6 static segments into separate arguments (`p.join(home, '.local', 'share', 'app', 'bin', 'config.json')`) causes `dart format` to wrap across 6–8 vertical lines and **destroys substring greppability** (`grep` / `code_search` for `.local/share/app/bin`).
* **Rule for POSIX Targets**: Prefer **2-argument boundary joining** (`p.join(home, '.local/share/app/bin/config.json')`). This prevents duplicate-slash bugs (`//`) at variable boundaries while preserving single-line readability and exact string searchability.

### Normalization vs. Canonicalization (`p.normalize` vs. `p.canonicalize`)
* `p.normalize(path)` resolves `.` and `..` segments purely lexically without consulting the filesystem or standardizing case.
* When deduplicating directory paths or comparing physical file identity across symlinks, relative roots, or case-insensitive filesystems, use `p.canonicalize(path)`.

### Strip Location Specifiers & Convert URIs Safely
* Strings formatted as `<path>:<line>-<col>` or `<path>:<line>` are not pure file paths. Passing them directly to `p.normalize` or `Uri.parse` causes bugs (on Windows, `Uri.parse` mistakes `C:` for a URI scheme and `:line` for a port).
* Extract the trailing `:line-col` suffix via regular expression (`RegExp(r'^(.*?):(\d+(?:-\d+)?)$')`) *before* passing the file path to `package:path`.
* **URI Boundary Conversions**: When converting between file paths and `Uri` objects, always use `p.toUri(path)` and `p.fromUri(uri)` rather than `Uri.parse(path)` or manual string concatenation.

---

## 2. Recommended package:path Idioms vs. String Anti-Patterns

### Path Joining
* **Prefer**: `p.join(dir, file)`
* **Avoid**: `'$dir/$file'` or `'a/$b'`
* **Why**: String interpolation injects `/` on Windows and creates duplicate
  slashes (`//`) when `$dir` ends with a trailing separator.

### Segment Matching
* **Prefer**: `p.split(path).contains('foo')`
* **Avoid**: `path.contains('foo/')`
* **Why**: String matching fails on Windows backslashes (`foo\bar`) and produces
  false positives on partial substring names (e.g. `barfoo/`).

### Root and Directory Prefixes
* **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))`).

### File Extensions
* **Prefer**: `p.extension(path) == '.wasm'`
* **Avoid**: `path.endsWith('.wasm')`
* **Why**: Substring suffix matching falsely matches directories (`foo.wasm/`)
  or non-extension suffixes.

### Extension Slicing and Compound Extensions
* **Prefer**: `p.withoutExtension(path)` and `p.extension(path, 2)`
* **Avoid**: `path.lastIndexOf('.')` and manual `substring` slicing
* **Why**: Manual arithmetic breaks on hidden dotfiles (`.gitignore`) and
  compound extensions (`.js.map`, `.tar.gz`).

### POSIX and URL Path Conversion
* **Prefer**: `p.posix.joinAll(p.split(path))` or `p.url.joinAll(p.split(path))`
* **Avoid**: `path.replaceAll(r'\', '/')`
* **Why**: Ad-hoc separator replacement fails on root drives and mixes OS
  context with POSIX or URL targets.

### URI Conversion
* **Prefer**: `p.toUri(path)` and `p.fromUri(uri)`
* **Avoid**: `Uri.parse(path)` and `uri.path`
* **Why**: Direct URI parsing fails on Windows drive letters (`C:`) and leaks
  percent-encoding (e.g. `%20` for spaces).

### Directory Basename Helper
* **Prefer**:
  `String canonicalDirName(Directory d) => p.basename(p.normalize(d.absolute.path));`
* **Avoid**: Repeating `p.basename(p.normalize(dir.absolute.path))` inline
  across files.
* **Why**: Centralizes canonical directory naming logic and reduces boilerplate.

---

## 3. Bridging Native Paths to POSIX, Git, & URL Contexts

Avoid calling `.replaceAll('\\', '/')` or `.replaceAll(r'\', '/')` to convert
OS-native paths into POSIX paths (for Git, YAML, archive manifests) or URL
segments.

**Rule**: Split the relative native path using `p.split(...)`, inspect segments
with **Dart 3 list pattern matching**, and join using `p.posix.joinAll(...)` or
`p.url.joinAll(...)`. Always call `p.relative(filePath, from: root)` first so
leading root segments (`'/'` on POSIX or `r'C:\'` on Windows) do not interfere
with relative prefix patterns:

```dart
import 'package:path/path.dart' as p;

String computeWebAssetKey(String filePath, String projectRoot) {
  final relative = p.relative(filePath, from: projectRoot);
  final segments = p.split(relative);
  return switch (segments) {
    ['assets', ...] => p.posix.joinAll(segments),
    _ => p.posix.joinAll(['assets', ...segments]),
  };
}
```

### Git Paths and Repository Metadata
* Git repository tree objects, `.gitignore` pattern rules, `.gitattributes`,
  and git-tracked symlinks strictly use POSIX forward slashes (`/`), even on
  Windows.
* Inserting native Windows backslashes (`\`) into `.gitignore` or git commands
  causes Git to treat `\` as an escape character rather than a directory
  separator, silently breaking pattern matching.
* When generating `.gitignore` entries, repository manifests, or symlink
  targets programmatically from native file paths, convert the relative native
  path using `p.posix.joinAll(p.split(relativePath))` or `p.posix.join(...)`.

---

## 4. Mockable File Systems (`package:file` vs. Global `p.*`)

In codebases that use `package:file` (e.g., CLI applications or services tested
with `MemoryFileSystem`), avoid calling top-level `p.*` functions on `File` or
`Directory` paths.

* Top-level `p.*` functions bind to the *host operating system* running the test.
* If a unit test creates a `MemoryFileSystem(style: FileSystemStyle.windows)` on a Linux or macOS runner, global `p.split(file.path)` will split on `/` instead of `\`, breaking the test.

**Rule**: Always use the `Context` attached to the `FileSystem` (`file.fileSystem.path`):

```dart
import 'package:file/file.dart';

List<String> listSubdirectoryNames(Directory dir) {
  final pathContext = dir.fileSystem.path;
  return dir
      .listSync()
      .whereType<Directory>()
      .map((d) => pathContext.basename(d.path))
      .toList();
}
```

---

## 5. Extensions, Compound Extensions & Stem Extraction

Avoid manual `.lastIndexOf('.')` and `.substring()` arithmetic when extracting file extensions or inserting content hashes. `p.extension` natively supports multi-level extensions via its optional `level` parameter.

* **Multi-Dot Stem Nuance**: Calling `p.extension('main.dart.wasm', 2)` returns `'.dart.wasm'` because it blindly captures the last two dot-separated segments. When hashing or stripping extensions on files that may have multi-dot stems (e.g., `main.dart.wasm` vs. `main.dart.js.map`), check whether `p.extension(filename, 2)` matches a known compound extension (or `.endsWith('.map')`) before falling back to single-level `p.extension(filename)`:

```dart
import 'package:path/path.dart' as p;

String insertContentHash(String filename, String hash) {
  final compoundExt = p.extension(filename, 2);
  // Only use the 2-level extension for true compound suffixes (e.g., '.js.map')
  final ext = compoundExt.endsWith('.map')
      ? compoundExt
      : p.extension(filename);
  final stem = filename.substring(0, filename.length - ext.length);
  return '$stem.$hash$ext';
}
```

---

## 6. Workflows & Audit Checklist

### Path Refactoring Checklist
- [ ] Replace string interpolation (`'$dir/$file'`) with `p.join(dir, file)`.
- [ ] Replace `.contains('dir/')` and `.startsWith('dir/')` with `p.split(path)` list pattern checks (`case ['dir', ...final rest]`) or `p.isWithin(parent, child)`.
- [ ] 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])`.
- [ ] Verify that code using `package:file` accesses `fileSystem.path` instead of global `p.*`.
- [ ] Ensure Git paths, `.gitignore` entries, and symlink targets use `p.posix` forward slashes.

---

## References & Examples

* **Cross-Platform Path & POSIX Conversion Examples**: [examples/cross_platform_paths.dart](examples/cross_platform_paths.dart)
* **Mockable FileSystem Path Context Example**: [examples/file_system_context.dart](examples/file_system_context.dart)

© flutter · BSD-3-Clause · flutter/agent-plugins

See also

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor