kRiverSkillContent top-level constant
String
const kRiverSkillContent
Implementation
const String kRiverSkillContent = r'''
---
name: river-cli-flutter
description: Build Flutter features using the river_cli scaffolding tool and the project's reusable widget library. ALWAYS use this skill when working in a Flutter project that uses river_cli, Riverpod, or go_router — including when the user asks to create a new screen, page, feature, model, repository, widget, or flow; implement a Figma design; add UI; or refactor features. Use it even if the user doesn't mention river_cli by name. Never manually create feature folders, controllers, models, or routes in these projects — this skill defines the required workflow.
---
# river_cli Flutter Development
This skill defines how to build features in Flutter projects scaffolded with
[river_cli](https://pub.dev/packages/river_cli) (Riverpod + go_router, modular
`lib/` structure with reusable widgets, networking, and storage modules).
**Golden rule: scaffold with the CLI, compose with the widget library, style
with the design tokens. Never hand-roll any of the three.**
## 1. Detect the project setup
Before writing code, run the built-in health check — it reports the package
name, which modules are installed, missing dependencies, and scaffolded
features in one shot:
```bash
river_cli doctor
```
If `doctor` reports a brand-new/empty project, initialize it first (ask the
user which modules they want if unclear):
```bash
dart pub global activate river_cli # if not installed
river_cli init --all # or --minimal, or --modules config,utils,widgets
```
Available modules: `core` (always), `config`, `utils`, `extensions`,
`widgets`, `network`, `storage`. Existing files are never overwritten unless
`--force` is passed — do not pass `--force` without explicit user permission.
## 2. Creating things — ALWAYS use the CLI
Every generator accepts `--path <dir>` (default `lib/presentation`),
`--force` (overwrite), and `--dry-run` (preview without writing). Prefer
`--dry-run` first when you are unsure what will be generated.
```bash
# Page with automatic go_router route registration
river_cli create page:<snake_case_name>
# Screen WITHOUT route registration (tabs, bottom-sheet content, sub-views)
river_cli create screen:<snake_case_name>
# Immutable data model with fromJson/toJson/copyWith, generated FROM A FIELD SPEC
river_cli create model:<name> --fields "title:String, done:bool, age:int?"
# …or infer the model (incl. nested models) from a JSON sample
river_cli create model:<name> --json '{"id":1,"name":"x","address":{"city":"y"}}'
# Repository wired to the network BaseRepository pattern
river_cli create repository:<name>
# Reusable widget placed in lib/app/shared_widgets/ following the library style
river_cli create widget:<name>
# FULL CRUD feature: model + repository + AsyncNotifier controller + view + route
river_cli create feature:<name> --fields "title:String, done:bool"
```
The `--fields` spec is `name:Type` pairs separated by commas; a trailing `?`
marks a field nullable (`age:int?`). Supported types include `String`, `int`,
`double`, `bool`, `DateTime`, and `List<T>`. Use it whenever the user describes
the shape of the data — do not hand-write model boilerplate. When the user
provides a JSON payload (or an API response), prefer `--json`/`--from-json` to
infer the model and any nested models. Add `--with-test` to also emit a
round-trip test.
Maintenance commands:
```bash
river_cli generate routes # rebuild lib/app/routes/ from features on disk
river_cli remove feature:<name> # delete a feature + its model/repo + route
```
`generate routes` overwrites the routing files (no custom guards preserved) —
use it to repair/sync. `remove` confirms before deleting; pass `--yes` to skip.
`page:` / `screen:` / `feature:` generate:
```
lib/<path>/<name>/
├── controllers/<name>_controller.dart # Riverpod controller
├── bindings/<name>_binding.dart
└── views/<name>_view.dart
```
After scaffolding:
- Put ALL state and business logic in the controller. Views stay declarative
and read state via `ref.watch`.
- Do not move, rename, or restructure the generated folders.
- The CLI registers routes for `page:`/`feature:` automatically — do not edit
the routing files manually except for guards, redirects, or nested routes.
## 3. UI composition — use the widget library, never raw Material
When the `widgets` module is present, these components exist and MUST be used
instead of their Material equivalents:
| Use this | Instead of |
| ------------------- | ----------------------------------- |
| `MyText` | `Text` |
| `CustomButton` | `ElevatedButton` / `TextButton` |
| `MyContainer` | `Container` (for styled boxes) |
| `InputTextField` | `TextField` / `TextFormField` |
| `CustomAssetImage` / `CustomAssetIcon` | `Image.asset` / `Icon` |
| `AppRefreshIndicator` | `RefreshIndicator` |
| `AppErrorBanner` / `EmptyStateWidget` / `CustomLoadingSpinner` | ad-hoc error/empty/loading UI |
Before building ANY new widget, run `river_cli create widget:<name>` or list
`lib/app/shared_widgets/` and reuse what is there. Only create a new shared
widget if nothing fits, and follow the naming style of the existing files.
## 4. Styling — design tokens only
When the `config` module is present:
- Colors: `AppColors.*` only. Never hardcode `Color(0xFF...)` or `Colors.*`.
The primary color is runtime-swappable — assume it can change.
- Text styles: `AppTextStyles.*` only.
- Strings shown to users: `AppStrings.*` (add new constants there).
- Constants/keys: `AppConstants`, `LocalDataKey` enum for local storage keys.
- Sizing: the project uses `sizer` — use responsive units and the `num.height`
/ `num.width` extensions rather than fixed pixel values where layout scales.
- Environment values come from `AppEnvironment` / `.env` via `Globals` —
never hardcode URLs, API keys, or environment-specific values.
## 5. Networking and storage
- All HTTP goes through the Dio `APIProvider` + `APIRequestRepresentable` +
repository pattern (`BaseRepository`). Add endpoints to `ApiEndPoints`.
Generate repositories with `river_cli create repository:<name>`. Never
instantiate Dio or http clients directly in controllers or views.
- Errors from the network layer are typed — handle them with the provided
exception types and surface them via `AppErrorBanner`.
- Local persistence: `LocalDB` (typed SharedPreferences wrapper with Riverpod
providers) for non-sensitive data, `SecureStorageService` for tokens and
secrets. Keys go in the `LocalDataKey` enum.
## 6. Figma-to-Flutter workflow
When implementing a Figma design (via the Figma MCP server or screenshots):
1. **Plan first, in plan mode if available.** List the screens in the flow,
identify UI elements that repeat, and map each element to an existing
`lib/app/shared_widgets/` component or propose a new shared widget. Present
this component inventory to the user before generating code.
2. **Map design tokens.** Match Figma colors/text styles to `AppColors` /
`AppTextStyles`. If a token is missing, add it to the config files.
3. **Model the data** behind each screen with `river_cli create model:` or a
full `create feature:` when there is a backing list/CRUD flow.
4. **Scaffold every screen** with `river_cli create page:` (or `screen:`).
5. **Build shared widgets first**, then compose screens from them.
6. **Verify**: run `dart format .` and `dart analyze` and fix all issues.
## 7. Quality gates (run before finishing any task)
```bash
river_cli doctor # confirm setup & dependencies
dart format .
dart analyze
flutter test # if tests exist
```
Code that fails analysis is not done. If `analysis_options.yaml` defines
strict lints, satisfy them rather than suppressing with `// ignore`.
## 8. User style preferences
- Prefer small, single-purpose widgets over large build methods; extract
widgets when a build method exceeds ~60 lines.
- No business logic in views — controllers only.
- Follow existing file naming exactly: `snake_case` files,
`<feature>_controller.dart`, `<feature>_view.dart`.
''';