diff --git a/.claude/commands/layout-debug.md b/.claude/commands/layout-debug.md new file mode 100644 index 0000000..e801d4c --- /dev/null +++ b/.claude/commands/layout-debug.md @@ -0,0 +1,8 @@ +--- +description: Diagnose and fix Flutter layout, overflow, and responsive UI issues. +argument-hint: [screen, widget, or layout issue] +--- + +Use the `layout-debug` skill to fix Flutter layout or responsive rendering issues. + +$ARGUMENTS diff --git a/.claude/commands/widget-test.md b/.claude/commands/widget-test.md new file mode 100644 index 0000000..5aab2e6 --- /dev/null +++ b/.claude/commands/widget-test.md @@ -0,0 +1,8 @@ +--- +description: Add or update Flutter widget tests using this template's test harness patterns. +argument-hint: [widget, screen, or behavior to cover] +--- + +Use the `widget-test` skill to add or update focused Flutter widget tests. + +$ARGUMENTS diff --git a/.claude/skills/layout-debug b/.claude/skills/layout-debug new file mode 120000 index 0000000..0ff119c --- /dev/null +++ b/.claude/skills/layout-debug @@ -0,0 +1 @@ +../../ai/skills/layout-debug \ No newline at end of file diff --git a/.claude/skills/widget-test b/.claude/skills/widget-test new file mode 120000 index 0000000..efdc1d7 --- /dev/null +++ b/.claude/skills/widget-test @@ -0,0 +1 @@ +../../ai/skills/widget-test \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 9f5cd7b..4c582b4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -59,6 +59,8 @@ Existing skills: - `review-pr-comments` — triage and resolve AI or human GitHub PR feedback - `create-pr` — verify, commit, push, and create or update a GitHub PR - `lint-format` — run Flutter/Dart formatting and analyzer checks +- `widget-test` — add or update focused widget tests using this template's Riverpod, localization, theme, and route patterns +- `layout-debug` — diagnose and fix Flutter overflow, constraints, scroll, and responsive layout issues - `build-verify` — full build/test/analyze/format pass (codegen + analyze + test, then iOS + Android builds in parallel, then `dart format`); auto-scopes to the diff and leaves the working tree dirty for review - `start-job` — run the post-spec implementation pipeline (`tasks` → `implement-tasks-sequence` → `build-verify` → `pr-review`) - `prd` — create a Flutter feature Product Requirements Document under `.claude/tasks//prd.md` diff --git a/ai/skills/feature-data-flow/SKILL.md b/ai/skills/feature-data-flow/SKILL.md index 2c29e1d..25e64f4 100644 --- a/ai/skills/feature-data-flow/SKILL.md +++ b/ai/skills/feature-data-flow/SKILL.md @@ -73,11 +73,15 @@ Feature UI - Use `@freezed` and generated `fromJson` when the DTO is serialized. - Keep DTO fields aligned with backend payload names and types. - When backend enums are involved, prefer resilient parsing patterns such as explicit unknown cases where needed. +- Use Dart pattern matching or guarded switches for backend variants, nullable fields, and enum-like + strings when that makes parsing more exhaustive and easier to audit. ## Entity Guidance - Put UI-facing or domain-facing models in `lib/common/data/entity/`. - Entities may differ from DTOs if the UI needs a cleaner or safer shape. - Prefer exposing entities to the rest of the app instead of leaking DTOs into widgets. +- Prefer explicit, exhaustive handling when mapping DTOs into entities. If an unknown backend value + is accepted, make the fallback visible in the mapper rather than burying it in UI code. ## Mapping Guidance - Put mapping in an extension on the DTO when the conversion is straightforward. diff --git a/ai/skills/feature-screen/SKILL.md b/ai/skills/feature-screen/SKILL.md index 10f9240..ddb47a3 100644 --- a/ai/skills/feature-screen/SKILL.md +++ b/ai/skills/feature-screen/SKILL.md @@ -40,7 +40,10 @@ For simple static screens, `*_state.dart` and `*_event.dart` may be unnecessary. 7. Register the route in `lib/app/navigation/app_router.dart`. 8. Use shared widgets and extensions before adding new abstractions. 9. If data is needed, add DTOs, entities, and use cases in the shared locations instead of doing IO in widgets. -10. Run `make gen`, then `fvm flutter analyze`, then relevant tests. +10. Check narrow and wide layout behavior for the page content. Use `LayoutBuilder` for parent + constraint-driven layouts, wrap flexible `Row` content in `Expanded`/`Flexible`, and avoid + fixed dimensions that can overflow localized text. +11. Run `make gen`, then `fvm flutter analyze`, then relevant tests. ## Conventions To Keep - Keep `*_page.dart` thin. @@ -63,3 +66,5 @@ For simple static screens, `*_state.dart` and `*_event.dart` may be unnecessary. - This template mixes fully-wired code with scaffolded placeholders. Check `lib/app/setup/setup_app.dart` before assuming a service is active. - Do not invent a `CustomScaffold`; current template screens use `Scaffold` directly. - Localization currently starts from `assets/localization/app_en.arb`. Add keys there and regenerate. +- If a screen overflows or needs meaningful responsive behavior, use the `layout-debug` skill before + final verification. diff --git a/ai/skills/implement/SKILL.md b/ai/skills/implement/SKILL.md index bf5c227..fa9e8f8 100644 --- a/ai/skills/implement/SKILL.md +++ b/ai/skills/implement/SKILL.md @@ -32,6 +32,8 @@ You are responsible for implementing one task from a Flutter feature plan while ## Applicable Skills - `feature-screen` — when the task adds or changes a screen, route, page split, UI state, or navigation. - `feature-data-flow` — when the task adds backend/storage data flow, DTOs, entities, use cases, or state that loads real data. +- `widget-test` — when the task changes shared UI, provider-driven visual states, or user-visible behavior that can be covered by a focused widget test. +- `layout-debug` — when the task involves overflow fixes, responsive behavior, scroll/constraint issues, or text clipping. - `lint-format` — when running standalone and the change does not need the broader `build-verify` suite. - `build-verify` — when running standalone and the task changed behavior, generated-code inputs, routes, platform setup, dependencies, tests, or other shared behavior. - `pr-review` — optional standalone final check when the user asks for a review after implementation. diff --git a/ai/skills/layout-debug/SKILL.md b/ai/skills/layout-debug/SKILL.md new file mode 100644 index 0000000..3c11578 --- /dev/null +++ b/ai/skills/layout-debug/SKILL.md @@ -0,0 +1,94 @@ +--- +name: layout-debug +description: > + Diagnose and fix Flutter layout, overflow, unbounded constraint, scroll, and responsive + rendering issues in this repository while preserving the template's existing feature, + theme, localization, and shared widget patterns. Use when UI overflows, text clips, + widgets disappear, layouts fail on mobile/tablet/desktop, or a feature needs responsive + behavior. +allowed-tools: Bash, Read, Grep, Glob, Edit, Write +model: claude-sonnet-4-6 +--- + +# Flutter Template Layout Debug + +Use this skill for UI fixes and responsive layout work in this Flutter template. + +## Read First +- `AGENTS.md` +- `docs/PROJECT_OVERVIEW.md` +- `docs/PROJECT_GUIDELINES.md` +- The affected `*_page.dart` and `*_page_content.dart` +- Shared widgets in `lib/common/component/` or `lib/common/composition/` used by the screen + +## Error Signatures +Map console/stack-trace error strings directly to fixes before broader diagnosis: + +- **"Vertical viewport was given unbounded height"** — a scrollable (`ListView`, `GridView`) + sits inside an unconstrained vertical parent (usually a `Column`). Wrap the scrollable in + `Expanded`, or give it an explicit height via `SizedBox`/`ConstrainedBox`. +- **"An InputDecorator...cannot have an unbounded width"** — a `TextField`/`TextFormField` sits + inside an unconstrained horizontal parent (usually a `Row`). Wrap it in `Expanded` or `Flexible`. +- **"RenderFlex overflowed by N pixels"** — a `Row`/`Column` child requests more space than the + parent allows (yellow/black stripes). Wrap the overflowing child in `Expanded` or `Flexible`, + or let text wrap. +- **"Incorrect use of ParentData widget"** — `Expanded`/`Flexible` is not a direct child of a + `Row`/`Column`/`Flex`, or `Positioned` is not a direct child of a `Stack`. Move it so it is. +- **"RenderBox was not laid out"** — cascading side effect, not the root cause. Ignore it and + look further up the output for the primary constraint violation above. + +## Diagnosis Checklist +1. Reproduce or identify the failing viewport, text scale, platform, and state. +2. Inspect the nearest `Row`, `Column`, `Stack`, `ListView`, `SingleChildScrollView`, + `Expanded`, `Flexible`, and fixed-size widget. +3. Find whether the problem is: + - missing constraints + - conflicting constraints + - unbounded scrollable content + - fixed dimensions where content is dynamic + - text that cannot wrap or scale within its parent + - a `Stack` or overlay that visually collides with later content +4. Fix the closest layout cause instead of papering over the symptom with arbitrary sizes. + +## Responsive Guidance +- Prefer `LayoutBuilder` when a widget must adapt to the actual space its parent gives it. +- Prefer `MediaQuery.sizeOf(context)` only when the whole screen size matters. +- Use constrained widths for readable desktop/tablet content, but keep page sections unframed + unless an existing shared composition already frames that content. +- Use breakpoints only when the UI meaningfully changes. Avoid sprinkling one-off magic numbers + through child widgets. +- Keep page widgets thin; place responsive branching in `*_page_content.dart` or a small local + helper widget. + +## Constraint Patterns +- In a `Row`, wrap long text or flexible content with `Expanded` or `Flexible`. +- In a `Column`, avoid placing unconstrained scrollables directly inside another unbounded parent. +- Give grids and fixed-format controls stable dimensions with `aspectRatio`, `SliverGridDelegate`, + `ConstrainedBox`, or `SizedBox` when dynamic content would otherwise shift layout. +- Use `SingleChildScrollView` for small form-like pages that can overflow vertically. +- Use `CustomScrollView` or slivers for larger composed screens with independently sized sections. +- Use `SafeArea` or existing system-bar helpers when content collides with system UI. + +## Text And Accessibility +- Let user-facing strings wrap unless truncation is clearly intended. +- Use `maxLines` and `overflow` only when the design has a real truncation rule. +- Verify button labels, tab labels, and card text at narrow widths. +- Do not solve text overflow by scaling font size with viewport width. + +## Workflow +1. Read the affected widgets and shared components. +2. Identify the smallest widget that owns the broken constraints. +3. Patch the layout using existing theme/context extensions and shared widgets. +4. Add or update a widget test when the issue can be captured deterministically. +5. Run a narrow test when available: + ```bash + fvm flutter test test/path/to/file_test.dart + ``` +6. Run `fvm flutter analyze` when the fix touches source code. + +## Completion Criteria +- No overflow or unbounded-constraint failure remains for the reported state. +- The fix works for narrow mobile and wider tablet/desktop constraints when the screen supports + those platforms. +- Text remains readable, wraps or truncates intentionally, and does not overlap neighboring UI. +- The solution follows existing feature and shared-widget boundaries. diff --git a/ai/skills/techspec/SKILL.md b/ai/skills/techspec/SKILL.md index 2dc484c..12c92e1 100644 --- a/ai/skills/techspec/SKILL.md +++ b/ai/skills/techspec/SKILL.md @@ -63,6 +63,8 @@ Ask the user about any ambiguities: - State boundary: a single `@riverpod` notifier or multiple cooperating providers? Sync `Notifier` or `AsyncNotifier`? - Navigation: new `@RoutePage` route(s), modal vs. full-screen, deep-link entry? +- Layout: mobile-only or responsive across tablet/desktop/web? Any scroll, grid, or text-wrapping + constraints that should be specified before implementation? - Codegen scope: which generated inputs are involved (`@freezed` models with `fromJson`, `@riverpod`, `@RoutePage`, localization, assets) — this drives when `make gen` needs to run. - Testing strategy: which providers / use cases warrant unit tests; any widget tests required? diff --git a/ai/skills/upgrade/SKILL.md b/ai/skills/upgrade/SKILL.md index cae794f..37318e6 100644 --- a/ai/skills/upgrade/SKILL.md +++ b/ai/skills/upgrade/SKILL.md @@ -57,6 +57,48 @@ Use this skill for SDK and dependency upgrades in this repository. - generated native plugin files 12. Review GitHub Actions to ensure they still use the pinned project SDK. +## Dependency Conflict Workflow + +When `pub get`, `pub upgrade`, or code generation fails because of version solving: + +1. Read the full solver message and identify the smallest incompatible package chain. +2. Run: + ``` + fvm flutter pub outdated + ``` +3. Prefer changing direct dependencies in `pubspec.yaml` over editing transitive constraints. +4. Keep versioned pairs aligned, especially: + - `freezed` and `freezed_annotation` + - `json_serializable` and `json_annotation` + - `riverpod_generator`, `riverpod_annotation`, `flutter_riverpod`, and `riverpod_lint` + - `auto_route_generator` and `auto_route` +5. If a single direct dependency is blocking resolution, try the narrowest compatible constraint + first rather than a broad major-version sweep. +6. After minor/patch upgrades, run `fvm flutter pub upgrade --tighten` to raise the lower bounds + in `pubspec.yaml` to the versions actually resolved, keeping constraints honest. +7. If `pubspec.lock` changes after resolution, review whether the churn matches the intended + upgrade scope. Do not hand-edit the lockfile, with one exception: when a single package is + retracted or stuck in a conflict, delete only that package's block from `pubspec.lock` and + rerun `fvm flutter pub get` so pub re-resolves just that package. Never delete the whole + lockfile to force a clean resolve — that causes uncontrolled upgrades across the entire graph. +8. After dependency resolution succeeds, rerun `make gen` before judging analyzer errors. Generated + code and analyzer failures are often stale until codegen completes. + +## Static Analysis Fixes During Upgrades + +Analyzer and lint changes are expected after SDK or package upgrades. Handle them in this order: + +1. Run `fvm flutter analyze` and group failures by root cause. +2. Use mechanical fixes only when they are clearly safe: + ``` + fvm dart fix --dry-run + fvm dart fix --apply + ``` +3. Review the resulting diff carefully. Revert or adjust mechanical changes that weaken template + conventions, generated-code boundaries, or readability. +4. Fix remaining analyzer issues by following `docs/PROJECT_GUIDELINES.md` and nearby code. +5. Run `fvm dart format` only after source changes settle. + ## Common SDK Resolution Failure If `pub get` or the IDE package update reports an error like: diff --git a/ai/skills/widget-test/SKILL.md b/ai/skills/widget-test/SKILL.md new file mode 100644 index 0000000..1568ce8 --- /dev/null +++ b/ai/skills/widget-test/SKILL.md @@ -0,0 +1,102 @@ +--- +name: widget-test +description: > + Add or update Flutter widget tests in this repository using the existing test layout, + Riverpod provider overrides, generated localization, shared theme setup, and FVM test + commands. Use when a task asks for widget tests, UI regression coverage, test coverage for + a screen/component, or when implementation work changes visible Flutter behavior. +allowed-tools: Bash, Read, Grep, Glob, Edit, Write +model: claude-sonnet-4-6 +--- + +# Flutter Template Widget Tests + +Use this skill when adding focused widget coverage for a screen, shared component, or UI state in +this Flutter template. + +## Read First +- `AGENTS.md` +- `docs/PROJECT_OVERVIEW.md` +- `docs/PROJECT_GUIDELINES.md` +- Nearby tests under `test/` +- The widget, provider, and state files being covered + +## When To Add Widget Tests +- A shared widget gets new behavior, styling states, callbacks, or accessibility expectations. +- A feature screen adds meaningful loading, empty, error, or success states. +- A bug fix changes what the user sees or can tap. +- A regression would be cheap to catch with a `pumpWidget` test. + +Do not add broad snapshot-style tests that only restate the widget tree. Prefer behavior and +contract checks: visible text, enabled/disabled states, callbacks, navigation events, empty/error +rendering, and provider-driven state transitions. + +## Test Location +- Shared UI: `test/common/_test.dart` +- Feature UI: `test/features//_page_content_test.dart` +- Provider-heavy feature states may also need focused provider tests; keep them near the feature + test folder unless the repo already has a stronger local pattern. + +## Harness Pattern +Call `Configuration.setup(flavor: Flavor.develop)` at the top of `main()` before any test runs — +existing tests in `test/common/` do this and widgets that read configuration fail without it. + +Build the smallest wrapper that gives the widget the dependencies it expects: + +```dart +Widget buildSubject({ + List overrides = const [], +}) { + return ProviderScope( + overrides: overrides, + child: MaterialApp( + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + theme: AppTheme.getThemeData(brightness: Brightness.light), + home: const Scaffold( + body: SubjectWidget(), + ), + ), + ); +} +``` + +Adapt imports to the current code. If the existing nearby tests use `patrolWidgetTest`, +`pumpWidgetAndSettle`, or another helper, reuse that style instead of creating a parallel harness. + +## Riverpod Guidance +- Wrap provider-dependent widgets in `ProviderScope`. +- Override IO, auth, storage, and notifier providers rather than hitting real services. +- Keep overrides explicit in each test group so state does not leak between tests. +- For one-off event flows, assert the visible side effect when possible. If navigation/snackbars + are difficult to observe directly, test the notifier event separately and keep the page listener + thin. + +## Route And Localization Guidance +- If the widget uses `context.router`, prefer an AutoRoute-aware harness or test the lower-level + content widget instead of the route page. +- If the widget reads `context.locale`, include generated localization delegates. +- Add localization keys in `assets/localization/app_en.arb` and run `make gen` before relying on + generated getters. + +## Workflow +1. Read the widget and the closest existing tests. +2. Pick the smallest test subject that still covers the behavior. +3. Add or update test files under `test/`. +4. Use `tester.pumpWidget(...)`, `tester.pump()`, and `tester.pumpAndSettle()` intentionally; + avoid arbitrary waits. +5. Assert user-observable behavior with `find`, callback counters, and provider state. +6. Run the narrow test file first: + ```bash + fvm flutter test test/path/to/file_test.dart + ``` +7. Run the full test suite when the change touches shared UI or state: + ```bash + fvm flutter test + ``` + +## Completion Criteria +- Tests are deterministic and isolated. +- No real network, Firebase, storage, or platform side effects run during widget tests. +- Test names describe behavior, not implementation details. +- `fvm flutter test` or the relevant narrowed test command passes. diff --git a/docs/PROJECT_GUIDELINES.md b/docs/PROJECT_GUIDELINES.md index d92fba6..54d66f4 100644 --- a/docs/PROJECT_GUIDELINES.md +++ b/docs/PROJECT_GUIDELINES.md @@ -31,6 +31,7 @@ Not every feature needs every file. Simple screens in the template only use `*_p - `CustomAppBar` is a common default for top bars. - `Scaffold` is used directly in the current template. Do not assume a `CustomScaffold` exists. - Edge-to-edge behavior is coordinated through `CustomSystemBarsTheme` in `lib/app/theme/custom_system_bars_theme.dart`. +- Use `LayoutBuilder` when a widget must adapt to parent constraints, and use `MediaQuery.sizeOf(context)` only when the whole screen size matters. Prefer stable constraints (`Expanded`, `Flexible`, `ConstrainedBox`, grid delegates, or aspect ratios) over fixed dimensions that can overflow with localized text or narrow viewports. ## Shared UI: Components vs Compositions - Use `lib/common/component/` for smaller reusable building blocks such as buttons, app bars, inputs, tabs, avatars, and low-level display widgets. @@ -44,6 +45,7 @@ Not every feature needs every file. Simple screens in the template only use `*_p - Stateful feature providers commonly use `@riverpod` classes ending in `StateNotifier`. - Shared notifier helpers live in `lib/core/riverpod/state_handler.dart`. - `AsyncValueExtension.mapState` and `mapContentState` from `lib/common/extension/async_value.dart` are the standard loading, error, and empty-state helpers. +- Prefer Dart pattern matching and sealed/freezed exhaustiveness when handling union states, events, and small branching models. Keep branches explicit when missing a case would change user-visible behavior. - Keep `@Riverpod(keepAlive: true)` for app-scoped state or long-lived services, not one-shot command providers. - For async use-case providers, prefer reading dependencies before the first `await` and continuing with captured objects instead of calling `ref` again later. - If an auto-dispose use-case truly must access `ref` after an async gap, keep it alive only for that operation with `final link = ref.keepAlive(); try { ... } finally { link.close(); }`. @@ -67,6 +69,7 @@ Not every feature needs every file. Simple screens in the template only use `*_p - Keep request and response payloads in `lib/common/data/dto/`. - Keep app-facing models in `lib/common/data/entity/`. - Map DTOs to entities close to the data layer instead of leaking transport models into widgets. +- When parsing backend variants, nullable fields, or enum-like strings, use explicit Dart pattern matching or guarded branches so unknown values are intentional rather than accidental. - Keep IO-oriented logic in Riverpod use cases under `lib/common/usecase/`. - Use `Flogger` when extra diagnostics are useful in service or integration code. @@ -120,6 +123,7 @@ Not every feature needs every file. Simple screens in the template only use `*_p - Treat `.fvmrc` and `pubspec.yaml` as the source of truth for SDK and package versions. - When upgrading Flutter, align the Flutter version in `pubspec.yaml` with `.fvmrc`. - After SDK or dependency upgrades, run `fvm flutter pub get`, `make gen`, `fvm flutter analyze`, and `fvm flutter test`. +- Resolve package conflicts from the smallest incompatible dependency chain first. Prefer updating direct constraints in `pubspec.yaml`, keep generator/runtime annotation pairs aligned, and let `pub` regenerate `pubspec.lock` instead of editing it by hand. - Re-check generator compatibility when touching versioned pairs such as: - `freezed` and `freezed_annotation` - `json_serializable` and `json_annotation` @@ -134,6 +138,7 @@ Not every feature needs every file. Simple screens in the template only use `*_p - `make integration_test` runs `patrol test --flavor develop`. - For Android integration tests, ensure `adb` is already available on your shell `PATH`. - Add or update tests when the task calls for behavior changes, bug fixes, or new features. Do not assume tests are off-limits. +- Prefer focused widget tests for shared UI, provider-driven visual states, and user-visible bug fixes. Call `Configuration.setup(flavor: Flavor.develop)` before tests that may read `Configuration.instance`, wrap provider-dependent widgets in `ProviderScope`, override IO-facing providers, include generated localization delegates when using `context.locale`, and avoid real network, Firebase, storage, or platform side effects in widget tests. ## Release And Versioning Notes - The repo contains release-oriented files and commands such as `release_notes.txt`, Android app bundle generation, and iOS IPA generation.