| name | jarvus-flutter |
| description | Mobile app development using Flutter + Riverpod + go_router. Use when creating Flutter apps, adding screens, managing state with Riverpod, implementing routing, building offline-first features, or setting up storybook component development. Also use when the user mentions Flutter, Dart, mobile apps, or cross-platform development. |
Mobile Flutter Stack
Cross-platform mobile app stack:
- Flutter - UI framework (iOS, Android, macOS, Web)
- Riverpod - State management (not Provider/ChangeNotifier)
- go_router - Declarative routing with deep linking
- dio - HTTP client with interceptors
- storybook_toolkit - Component development with device frames
No code generation. Models use hand-written fromJson/toJson.
Optional: drift (local SQLite)
For offline-first apps that need a local relational database, add drift. This is the one exception to the no-codegen rule — drift generates typed query code from table definitions. See offline-first.md for setup and patterns.
Environment Setup
Use asdf to manage Flutter and all tool versions:
asdf plugin add flutter
asdf set flutter latest
asdf set ruby latest
asdf set cocoapods latest
asdf set rust latest
This creates a .tool-versions file in the project root. Never use the official Flutter installer, mise, or other version managers.
MCP Setup
If .mcp.json doesn't exist in the project root, create it with this content:
{
"mcpServers": {
"dart": {
"type": "stdio",
"command": "dart",
"args": [
"mcp-server"
],
"env": {}
}
}
}
This wires up dart mcp-server so the agent can drive the app, inspect widgets, capture screenshots, and analyze files. See mcp-driving-and-screenshots.md for usage.
Reference Files
| File | When to Use |
|---|
| setup-guide.md | Starting a new Flutter project from scratch |
| patterns.md | Riverpod providers, repositories, screen architecture |
| offline-first.md | Adding drift for local SQLite, offline download/collect/sync |
| storybook.md | Setting up storybook, adding stories, mock data |
| gotchas.md | Common mistakes and platform-specific issues |
| mcp-driving-and-screenshots.md | Launching, driving (tap/type), debugging, and screenshotting a running app via the Dart MCP — and delivering screenshots to the user |
| mcp.json | Sample .mcp.json for the project root — copy here if it doesn't exist |
Companion Skills
This skill defines the stack; pair it with the foundational Dart & Flutter agent skills that
apply to essentially any project on it. Install them into a project with npx skills
(the agent is auto-detected, so -y runs non-interactively):
npx skills add dart-lang/skills \
-s dart-run-static-analysis \
-s dart-add-unit-test \
-s dart-fix-runtime-errors -y
npx skills add flutter/skills \
-s flutter-apply-architecture-best-practices \
-s flutter-add-widget-test \
-s flutter-add-integration-test -y
| Skill | Why it's foundational |
|---|
dart-run-static-analysis | dart analyze + dart fix --apply — during dev and before every commit |
dart-add-unit-test | unit tests for logic with package:test |
dart-fix-runtime-errors | stack-trace → fix → hot_reload loop |
flutter-apply-architecture-best-practices | layered UI/logic/data structure (complements patterns.md) |
flutter-add-widget-test | component-level tests with WidgetTester |
flutter-add-integration-test | Flutter Driver / integration_test harness — pairs with mcp-driving-and-screenshots.md |
Everything else is project-dependent — pick based on what the app actually does (JSON
serialization, declarative routing, localization, responsive layout, HTTP, drift, coverage,
mocks, pattern matching, …). Browse the full catalogs and --list to choose:
Quick Reference
Commands
flutter pub get
flutter run -d macos
flutter run -d macos -t lib/storybook/main.dart
flutter run -d chrome
flutter analyze lib/
Package Management
Always use CLI commands — never edit pubspec.yaml directly:
flutter pub add <package>
flutter pub add --dev <package>
flutter pub add --dev storybook_toolkit --git-url https://github.com/org/repo.git --git-ref branch-name
Project Structure
├── lib/
│ ├── main.dart # App entrypoint
│ ├── app.dart # MaterialApp.router + GoRouter + theme
│ ├── models/ # Plain Dart classes with fromJson/toJson
│ ├── providers/ # Riverpod providers
│ ├── repositories/ # Abstract interfaces + implementations
│ ├── database/ # drift tables + generated code (if using drift)
│ ├── screens/ # One directory per route
│ ├── storybook/ # Storybook entrypoint + mock data
│ ├── widgets/ # Shared reusable widgets
│ └── constants/ # Theme, colors, typography
├── specs/ # Feature specifications (if using spec-first)
├── .env # Environment config (gitignored)
├── .env.example # Template
└── pubspec.yaml
Architecture Layers
Screens (widgets) → read from providers, dispatch actions
Providers (Riverpod) → async state, business logic
Repositories (abstract) → data access boundary
API Client / Local DB → external boundaries
Screens never call repositories directly — always through providers. Repositories are abstract interfaces with real and fake implementations. Fakes are injected via Riverpod provider overrides in storybook and tests.
Key Patterns
// Riverpod provider
final stationsProvider = FutureProvider<List<Station>>((ref) {
return ref.watch(stationRepositoryProvider).getAll();
});
// Family provider (parameterized)
final stationProvider = FutureProvider.family<Station?, String>((ref, id) {
return ref.watch(stationRepositoryProvider).get(id);
});
// Consumer widget
class MyScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final dataAsync = ref.watch(stationsProvider);
return dataAsync.when(
data: (stations) => ListView(...),
loading: () => CircularProgressIndicator(),
error: (e, _) => Text('Error: $e'),
);
}
}
// Navigation with go_router
context.push('/items/${item.id}'); // push onto stack
context.go('/login'); // replace stack
Common Gotchas
-
Package management: Use flutter pub add, never edit pubspec.yaml directly
-
VSCode SDK path: Use ~ not ${env:HOME} in .vscode/settings.json:
{"dart.flutterSdkPath": "~/.asdf/installs/flutter/3.41.6-stable"}
-
macOS entitlements: Network requests need com.apple.security.network.client, keychain needs keychain-access-groups — both in macos/Runner/DebugProfile.entitlements and Release.entitlements
-
macOS signing: Set development team in Xcode (Runner target → Signing & Capabilities → Team → All configurations)
-
Commit generated code separately: After any command that generates/changes code, commit those changes with the exact command in the message BEFORE making manual edits
CI & Code Quality
Wire analyze, format, and tests into CI from the start. The cross-cutting CI harness — asdf
provisioning + caching, path-filtered workflows, the setup-asdf composite — lives in the
ci-quality-gates skill; reuse it here. The tools differ from the TS/Python stacks:
Dart ships its own analyzer and formatter, so there's no oxc/ruff here.
The gate (Dart-native):
| Gate | Command |
|---|
| Lint / analyze | flutter analyze lib/ test/ |
| Format check | dart format --output=none --set-exit-if-changed lib/ test/ |
| Test | flutter test |
flutter analyze reads analysis_options.yaml — start from package:flutter_lints (and add
riverpod_lint only if it doesn't clash with drift_dev's analyzer version; see
gotchas.md). The companion dart-run-static-analysis skill (see
Companion Skills above) drives dart analyze + dart fix --apply locally — the same gate,
just in the dev loop.
Provisioning. Flutter is pinned in .tool-versions (plus ruby/cocoapods for iOS), so the
setup-asdf composite from ci-quality-gates provisions CI exactly as for the other stacks.
Commit pubspec.lock and use flutter pub get (it respects the lock). The credential-free,
path-filtered, IDE-over-pre-commit principles all carry over — only the tool commands change.