---
name: flutter-architecture-conventions
description: Applies the shared feature-first Flutter architecture, UI, state, error, routing, and testing conventions used across the field guides. Use when starting or reviewing a Flutter feature, deciding where code belongs, or keeping implementation consistent across an app workspace.
---

# Flutter Architecture Conventions

## Quick start

1. Put shared theme, network, UI atoms, utilities, and global models in the shared package.
2. Put business behavior in the main app under `features/<feature_name>/`.
3. Split each feature into `models/`, `controllers/`, `repositories/`, and `views/`.
4. Keep views logic-less, controllers stateful, and repositories responsible for external data.
5. Add focused unit tests before considering the feature complete.

## Dependency direction

- The main app may depend on the shared package; the shared package must never depend on the main app.
- Shared infrastructure belongs in `core/`; feature-specific code stays inside its feature.
- Use immutable domain models with `const` constructors and `fromJson`/`toJson`; do not add parallel DTO layers.
- Repositories return domain models, not raw response maps.

## Build a feature

1. Define the immutable model and serialization.
2. Implement the repository as the single source of truth for API, cache, and parsing behavior.
3. Expose the repository with Riverpod; pass `Ref` when the repository needs shared clients or services.
4. Implement a `Notifier` or `AsyncNotifier` controller that owns state transitions and commands.
5. Build the view as a `HookConsumerWidget`; use hooks only for ephemeral UI state.
6. Register routes centrally while keeping route identity on the destination screen.
7. Test repository mapping and controller loading, data, and failure states.

## UI rules

- Prefer shared factories such as `AppText`, `AppBtn`, `AppBottomSheet`, and `AppSnack`.
- Use context extensions for theme and dimensions, and `gap` spacing such as `16.gap`.
- Buttons that invoke a `Future` must own their loading state and notify `onLoading` around the awaited action.
- Extract class widgets instead of `_buildWidget(context)` helper methods.
- Use absolute `package:` imports; never use relative imports.

## Cross-cutting rules

- Use plain Dart providers and routes by default; introduce code generation only when it removes meaningful complexity.
- Map infrastructure exceptions into one sealed `Failure` vocabulary at repository boundaries.
- Route errors through one reporter and keep raw exceptions out of the UI.
- Give every screen static `routeName` and `routeLocation` constants.
- Use `StatefulShellRoute.indexedStack` for state-preserving bottom navigation.
- Record meaningful success events and named screen views alongside failures.

## Definition of done

- Business logic has Given-When-Then unit coverage with `mocktail` or provider overrides.
- Critical flows have Patrol coverage where appropriate.
- Loading, empty, error, retry, and success behavior are deliberate.
- New imports preserve package boundaries and use absolute paths.
- The relevant monthly changelog records the change.
