Back to skills
SkillHub ClubWrite Technical DocsFull StackFrontendTech Writer

flutter-dev

Expert guidance for Flutter and Dart development. Use when building Flutter apps, implementing state management, setting up routing, writing tests, or working with Flutter UI components. Provides access to detailed rules for Bloc, Riverpod, Provider, Mocktail, and more.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
27
Hot score
88
Updated
March 20, 2026
Overall rating
C2.7
Composite score
2.7
Best-practice grade
B75.6

Install command

npx @skill-hub/cli install calvingit-ai-tools-flutter-dev

Repository

calvingit/ai-tools

Skill path: skills/flutter-dev

Expert guidance for Flutter and Dart development. Use when building Flutter apps, implementing state management, setting up routing, writing tests, or working with Flutter UI components. Provides access to detailed rules for Bloc, Riverpod, Provider, Mocktail, and more.

Open repository

Best for

Primary workflow: Write Technical Docs.

Technical facets: Full Stack, Frontend, Tech Writer, Testing.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: calvingit.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install flutter-dev into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/calvingit/ai-tools before adding flutter-dev to shared team environments
  • Use flutter-dev for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: flutter-dev
description: Expert guidance for Flutter and Dart development. Use when building Flutter apps, implementing state management, setting up routing, writing tests, or working with Flutter UI components. Provides access to detailed rules for Bloc, Riverpod, Provider, Mocktail, and more.
---

# Flutter Development Skill

## Overview

This Skill provides modular Flutter development guidance with access to detailed documentation for specific libraries and patterns.

**Quick Start**: When user mentions a specific library (e.g., "use Bloc", "Riverpod", "GoRouter"), read the corresponding `./rules/*.md` file before responding.

---

## Module 1: State Management

### Core Principles
- Prefer built-in solutions for simple state (ValueNotifier, ChangeNotifier, StreamBuilder)
- Choose state management based on complexity and team familiarity
- Read detailed rules when using specific libraries

### Extended Rules Index

| Priority | Library | Rule File | Trigger Keywords | Notes |
|----------|---------|-----------|------------------|-------|
| πŸ”₯ High | Bloc | `./rules/bloc.md` | "bloc", "cubit", "blocprovider" | Event-driven, enterprise scale |
| πŸ”₯ High | Riverpod | `./rules/riverpod.md` | "riverpod", "provider 2.0" | Modern, type-safe |
| 🟑 Medium | Provider | `./rules/provider.md` | "provider" | Classic, widely used |
| 🟒 Low | ChangeNotifier | `./rules/flutter_change_notifier.md` | "changenotifier" | Built-in, simple |

**Usage**: Read the rule file when user mentions trigger keywords.

### Quick Reference

**For simple state**: Start with ChangeNotifier or ValueNotifier
**For complex apps**: Use Bloc or Riverpod
**When unsure**: Ask user about their preferences and app complexity

---

## Module 2: Testing

### Core Principles
- Write tests that can actually fail if real code is broken
- Use Mocktail for mocks (preferred over Mockito)
- Always use `group()` for test organization

### Extended Rules Index

| Priority | Topic | Rule File | Trigger Keywords | Notes |
|----------|-------|-----------|------------------|-------|
| πŸ”₯ High | Mocktail | `./rules/mocktail.md` | "mocktail", "mock", "fake" | Preferred mocking lib |
| 🟑 Medium | Mockito | `./rules/mockito.md` | "mockito" | Legacy mocking lib |
| 🟑 Medium | Testing | `./rules/testing.md` | "test", "testing", "widget test" | General testing guide |

### Testing Checklist

```
Test Quality Checklist:
- [ ] Test can fail if real code is broken
- [ ] Tests are grouped with group()
- [ ] Test names use "should" format
- [ ] Mocks are used appropriately
```

---

## Module 3: Navigation & Routing

### Core Principles
- Use GoRouter for all navigation (declarative approach)
- Prefer named routes over hardcoded paths
- Centralize route definitions

### Extended Rules Index

| Priority | Topic | Rule File | Trigger Keywords | Notes |
|----------|-------|-----------|------------------|-------|
| πŸ”₯ High | GoRouter | `./rules/navigation.md` | "router", "navigation", "gorouter", "route" | Declarative routing |

### Quick Reference

```dart
// Navigation actions
context.goNamed('profile')      // Switch screen
context.pushNamed('details')     // Stack screen
context.pop()                    // Go back
```

---

## Module 4: Code Quality & Standards

### Core Principles
- Follow Effective Dart guidelines
- Use `dart analyze` and `dart format` regularly
- Write self-documenting code with clear naming

### Extended Rules Index

| Priority | Topic | Rule File | Trigger Keywords | Notes |
|----------|-------|-----------|------------------|-------|
| πŸ”₯ High | Effective Dart | `./rules/effective_dart.md` | "style", "naming", "format", "effective dart" | Style guide |
| πŸ”₯ High | Dart 3 Updates | `./rules/dart_3_updates.md` | "dart 3", "patterns", "records", "class modifiers" | New features |
| 🟑 Medium | Code Review | `./rules/code_review.md` | "review", "code review" | Review guidelines |

### Naming Standards

| Type | Convention | Example |
|------|------------|---------|
| Classes | PascalCase | `UserRepository` |
| Variables/Functions | camelCase | `fetchUserData` |
| Files | snake_case | `user_repository.dart` |

### Code Quality Checklist

```
- [ ] Functions < 20 lines
- [ ] Single responsibility per function
- [ ] Descriptive names, no abbreviations
- [ ] Proper error handling with developer.log()
- [ ] Run: dart analyze && dart format .
```

---

## Module 5: Architecture & Data

### Core Principles
- Separate concerns: Presentation, Business Logic, Data layers
- Use repositories to abstract data sources
- Keep business logic out of UI widgets

### Extended Rules Index

| Priority | Topic | Rule File | Trigger Keywords | Notes |
|----------|-------|-----------|------------------|-------|
| πŸ”₯ High | App Architecture | `./rules/flutter_app_architecture.md` | "architecture", "project structure", "layer" | Project organization |
| 🟑 Medium | JSON Serialization | `./rules/json_serialization.md` | "json", "serializable", "json_serializable" | Data parsing |
| 🟑 Medium | Error Handling | `./rules/flutter_errors.md` | "error", "exception", "handling errors" | Error patterns |

### Architecture Layers

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Presentation Layer (UI)        β”‚
β”‚   - Widgets                      β”‚
β”‚   - Blocs/Cubits/Providers       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Business Logic Layer          β”‚
β”‚   - Use cases / Services         β”‚
β”‚   - Business rules               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Data Layer                     β”‚
β”‚   - Repositories                 β”‚
β”‚   - Data sources (API, DB)       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

---

## Common Workflows

### Workflow: New Flutter Feature

```
Feature Development Progress:
- [ ] 1. Define data models (with json_serializable if needed)
- [ ] 2. Create repository/service layer
- [ ] 3. Implement business logic (use appropriate state management)
- [ ] 4. Build UI widgets (break into small components)
- [ ] 5. Write tests (unit β†’ widget β†’ integration)
- [ ] 6. Run: dart analyze && dart format
```

**When user starts a new feature**: Reference this workflow and ask which step they're on.

### Workflow: Choosing State Management

```
State Management Decision Tree:
1. Is state simple and local? β†’ ValueNotifier / ChangeNotifier
2. Is state complex/shared? β†’ Bloc / Riverpod / Provider
3. Does user/event tracking matter? β†’ Bloc
4. Is type safety a priority? β†’ Riverpod
5. What does the team prefer? β†’ Ask user
```

### Workflow: Setting Up Navigation

```
GoRouter Setup Checklist:
- [ ] 1. Add go_router dependency
- [ ] 2. Create app_router.dart with route definitions
- [ ] 3. Use MaterialApp.router in main.dart
- [ ] 4. Define routes with GoRoute (path, name, builder)
- [ ] 5. Implement guards/redirect if needed
- [ ] 6. Test navigation flows
```

---

## Anti-patterns to Avoid

| Anti-pattern | Why Bad | Correct Approach |
|--------------|--------|------------------|
| Network calls in `build()` | Blocks UI, causes repeated calls | Use initState, FutureBuilder, or state management |
| `setState()` in StatelessWidget | Compile error | Use StatefulWidget or state management |
| Ignoring null safety (`!` without check) | Runtime crashes | Use nullable types or verify with `?.` |
| Deep widget trees | Hard to read, maintain | Extract widgets into smaller components |
| Using `print()` for logging | Not configurable, no context | Use `dart:developer` log() |
| Direct bloc-to-bloc communication | Tight coupling | Use shared repository or listener pattern |

---

## Troubleshooting Guide

### Common Errors

| Error | Cause | Solution |
|-------|-------|----------|
| `LateInitializationError` | Variable used before init | Initialize in constructor or make nullable |
| `RenderFlex overflowed` | Content exceeds bounds | Wrap in SingleChildScrollView or use ListView |
| `setState() called after dispose()` | Async callback after widget unmounted | Check: `if (mounted) setState(...)` |

### Debugging Commands

```bash
# Analyze code
dart analyze

# Format code
dart format .

# Run tests
flutter test

# Check dependencies
flutter pub deps
```

---

## Quick Command Reference

```bash
# Project creation
flutter create my_app

# Dependency management
flutter pub add <package>
flutter pub remove <package>
flutter pub upgrade

# Code quality
dart analyze
dart format .
dart fix --apply

# Testing
flutter test
flutter test --coverage
```

---

## References

- [evanca/flutter-ai-rules](https://github.com/evanca/flutter-ai-rules) - Source of these rules
- [Flutter Documentation](https://docs.flutter.dev/)
- [Dart Language Guide](https://dart.dev/guides)


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### rules/bloc.md

```markdown
# Bloc Rules

### Naming Conventions
1. Name events in the past tense, as they represent actions that have already occurred from the bloc's perspective.
2. Use the format: `BlocSubject` + optional noun + verb (event). Example: `LoginButtonPressed`, `UserProfileLoaded`
3. For initial load events, use: `BlocSubjectStarted`. Example: `AuthenticationStarted`
4. The base event class should be named: `BlocSubjectEvent`.
5. Name states as nouns, since a state is a snapshot at a particular point in time.
6. When using subclasses for states, use the format: `BlocSubject` + `Initial` | `Success` | `Failure` | `InProgress`. Example: `LoginInitial`, `LoginSuccess`, `LoginFailure`, `LoginInProgress`
7. For single-class states, use: `BlocSubjectState` with a `BlocSubjectStatus` enum (`initial`, `success`, `failure`, `loading`). Example: `LoginState` with `LoginStatus.initial`
8. The base state class should always be named: `BlocSubjectState`.

### Modeling State
1. Extend `Equatable` for all state classes to enable value equality.
2. Annotate state classes with `@immutable` to enforce immutability.
3. Implement a `copyWith` method in state classes for easy state updates.
4. Use `const` constructors for state classes when possible.
5. Use a single concrete class with a status enum for simple, non-exclusive states or when many properties are shared.
6. In the single-class approach, make properties nullable and handle them based on the current status.
7. Use a sealed class with subclasses for well-defined, exclusive states.
8. Store shared properties in the sealed base class; keep state-specific properties in subclasses.
9. Use exhaustive `switch` statements to handle all possible state subclasses.
10. Prefer the sealed class approach for type safety and exhaustiveness; prefer the single-class approach for conciseness and flexibility.
11. Always pass all relevant properties to the `props` getter when using Equatable in state classes.
12. When using Equatable, copy List or Map properties with `List.of` or `Map.of` to ensure value equality.
13. To retain previous data after an error, use a single state class with nullable data and error fields.
14. Emit a new instance of the state each time you want the UI to update; do not reuse the same instance.

### Bloc Concepts
1. Use `Cubit` for simple state management without events; use `Bloc` for more complex, event-driven state management.
2. Define the initial state by passing it to the superclass in both `Cubit` and `Bloc`.
3. Only use the `emit` method inside a `Cubit` or `Bloc`; do not call it externally.
4. UI components should listen to state changes and update only in response to new states.
5. Duplicate states (`state == nextState`) are ignored; no state change will occur.
6. Override `onChange` in `Cubit` or `Bloc` to observe all state changes.
7. Use a custom `BlocObserver` to observe all state changes and errors globally.
8. Override `onError` in both `Cubit`/`Bloc` and `BlocObserver` for error handling.
9. Add events to a `Bloc` in response to user actions or lifecycle events.
10. Use `onTransition` in `Bloc` to observe the full transition (event, current state, next state).
11. Use event transformers (e.g., debounce, throttle) in `Bloc` for advanced event processing.
12. Prefer `Cubit` for simplicity and less boilerplate; prefer `Bloc` for traceability and advanced event handling.
13. If unsure, start with `Cubit` and refactor to `Bloc` if needed as requirements grow.
14. Initialize `BlocObserver` in `main.dart` for debugging and logging.
15. Always keep business logic out of UI widgets; only interact with cubits/blocs via events or public methods.
16. Internal events in a bloc should be private and only used for real-time updates from repositories.
17. Use custom event transformers for internal events if needed.
17. When exposing public methods on a cubit, only use them to trigger state changes and return `void` or `Future<void>`.
18. For blocs, avoid exposing custom public methods; trigger state changes by adding events via the `add` method.
19. When using `BlocProvider.of(context)`, call it within a child `BuildContext`, not the same context where the bloc was provided.

### Architecture
1. Separate your features into three layers: Presentation, Business Logic, and Data.
2. The Data Layer is responsible for retrieving and manipulating data from sources such as databases or network requests.
3. Structure the Data Layer into repositories (wrappers around data providers) and data providers (perform CRUD operations).
4. The Business Logic Layer responds to input from the presentation layer and communicates with repositories to build new states.
5. The Presentation Layer renders UI based on bloc states and handles user input and lifecycle events.
6. Inject repositories into blocs via constructors; blocs should not directly access data providers.
7. Avoid direct bloc-to-bloc communication to prevent tight coupling.
8. To coordinate between blocs, use BlocListener in the presentation layer to listen to one bloc and add events to another.
9. For shared data, inject the same repository into multiple blocs; let each bloc listen to repository streams independently.
10. Always strive for loose coupling between architectural layers and components.
11. Structure your project consistently and intentionally; there is no single right way.

### Flutter Bloc Concepts
1. Use `BlocBuilder` to rebuild widgets in response to bloc or cubit state changes; the builder function must be pure.
2. Use `BlocListener` to perform side effects (e.g., navigation, dialogs) in response to state changes.
3. Use `BlocConsumer` when you need both `BlocBuilder` and `BlocListener` functionality in a single widget.
4. Use `BlocProvider` to provide blocs to widget subtrees via dependency injection.
5. Use `MultiBlocProvider` to provide multiple blocs and avoid deeply nested providers.
6. Use `BlocSelector` to rebuild widgets only when a selected part of the state changes.
7. Use `MultiBlocListener` to listen for state changes and trigger side effects; avoid nesting listeners by using `MultiBlocListener`.
8. Use `RepositoryProvider` to provide repositories or services to the widget tree.
9. Use `MultiRepositoryProvider` to provide multiple repositories and avoid nesting.
10. Use `context.read<T>()` to access a bloc or repository without listening for changes (e.g., in callbacks).
11. Use `context.watch<T>()` inside the build method to listen for changes and trigger rebuilds.
12. Use `context.select<T, R>()` to listen for changes in a specific part of a bloc’s state.
13. Avoid using `context.watch` or `context.select` at the root of the build method to prevent unnecessary rebuilds.
14. Prefer `BlocBuilder` and `BlocSelector` over `context.watch` and `context.select` for explicit rebuild scoping.
15. Scope rebuilds using `Builder` when using `context.watch` or `context.select` for multiple blocs.
16. Handle all possible cubit/bloc states explicitly in the UI (e.g., empty, loading, error, populated).

### Code Quality & Linting
1. Use the `bloc_lint` package to enforce Bloc best practices and catch common mistakes.
2. Use `bloc_tools` command-line utilities for project scaffolding and development workflow.
3. Configure `bloc_lint` in your `analysis_options.yaml` with recommended rules for static analysis.
4. Avoid importing Flutter dependencies in your business logic components (blocs, cubits, repositories).
5. Avoid exposing public fields on Bloc and Cubit instances; all state should be accessed through the `state` getter.
6. Avoid exposing public methods on Bloc instances; use the `add` method to communicate with blocs via events.
7. Use `bloc lint` command from `bloc_tools` to run linting from the command line.
8. Install `bloc_tools` globally for command-line access: `dart pub global activate bloc_tools`.
9. Use the recommended `analysis_options.yaml` configuration with all recommended bloc linting rules enabled.
10. Available lint rules include: `avoid_flutter_imports`, `avoid_public_fields`, `avoid_public_bloc_methods`, `prefer_bloc`, `prefer_cubit`, and `prefer_void_public_cubit_methods`.

### Developer Tools & Integrations
1. Create custom templates using Mason bricks for consistent code generation across projects.
2. Use the official Bloc developer tools for debugging and state inspection.

### Testing
1. Add the `test` and `bloc_test` packages to your dev dependencies for bloc testing.
2. Organize tests into groups to share setup and teardown logic.
3. Create a dedicated test file (e.g., `counter_bloc_test.dart`) for each bloc.
4. Import the `test` and `bloc_test` packages in your test files.
5. Use `setUp` to initialize bloc instances before each test and `tearDown` to clean up after tests.
6. Test the bloc’s initial state before testing transitions.
7. Use the `blocTest` function to test bloc state transitions in response to events.
8. Assert the expected sequence of emitted states for each bloc event.
9. Keep tests concise, focused, and easy to maintain to ensure confidence in refactoring.
10. Mock cubits/blocs in widget tests to verify UI behavior for all possible states.
11. Consider using `bloc_lint` in your CI/CD pipeline to enforce code quality standards.

### Ecosystem & Packages
1. The Bloc ecosystem consists of multiple packages: `bloc` (core), `flutter_bloc` (Flutter widgets), `bloc_test` (testing), `bloc_lint` (linting), `bloc_tools` (CLI), `bloc_concurrency` (event transformers), `hydrated_bloc` (persistence), `replay_bloc` (undo/redo), and `angular_bloc` (AngularDart).
2. Use `bloc_concurrency` package for advanced event transformers like `sequential`, `concurrent`, `droppable`, and `restartable`.
3. Use `hydrated_bloc` for automatic state persistence and restoration across app sessions.
4. Use `replay_bloc` to add undo and redo functionality to your blocs.
5. Install packages based on your needs: start with `bloc` and `flutter_bloc` for basic functionality.


```

### rules/riverpod.md

```markdown
# Riverpod Rules

### Using Ref in Riverpod
1. The `Ref` object is essential for accessing the provider system, reading or watching other providers, managing lifecycles, and handling dependencies in Riverpod.
2. In functional providers, obtain `Ref` as a parameter; in class-based providers, access it as a property of the Notifier.
3. In widgets, use `WidgetRef` (a subtype of `Ref`) to interact with providers.
4. The `@riverpod` annotation is used to define providers with code generation, where the function receives `ref` as its parameter.
5. Use `ref.watch` to reactively listen to other providers; use `ref.read` for one-time access (non-reactive); use `ref.listen` for imperative subscriptions; use `ref.onDispose` to clean up resources.
6. Example: Functional provider with Ref
   ```dart
   final otherProvider = Provider<int>((ref) => 0);
   final provider = Provider<int>((ref) {
     final value = ref.watch(otherProvider);
     return value * 2;
   });
   ```
7. Example: Provider with @riverpod annotation
   ```dart
   @riverpod
   int example(ref) {
     return 0;
   }
   ```
8. Example: Using Ref for cleanup
   ```dart
   final provider = StreamProvider<int>((ref) {
     final controller = StreamController<int>();
     ref.onDispose(controller.close);
     return controller.stream;
   });
   ```
9. Example: Using WidgetRef in a widget
   ```dart
   class MyWidget extends ConsumerWidget {
     @override
     Widget build(BuildContext context, WidgetRef ref) {
       final value = ref.watch(myProvider);
       return Text('$value');
     }
   }
   ```

### Combining Requests
1. Use the `Ref` object to combine providers and requests; all providers have access to a `Ref`.
2. In functional providers, obtain `Ref` as a parameter; in class-based providers, access it as a property of the Notifier.
3. Prefer using `ref.watch` to combine requests, as it enables reactive and declarative logic that automatically recomputes when dependencies change.
4. When using `ref.watch` with asynchronous providers, use `.future` to await the value if you need the resolved result, otherwise you will receive an `AsyncValue`.
5. Avoid calling `ref.watch` inside imperative code (e.g., listener callbacks or Notifier methods); only use it during the build phase of the provider.
6. Use `ref.listen` as an alternative to `ref.watch` for imperative subscriptions, but prefer `ref.watch` for most cases as `ref.listen` is more error-prone.
7. It is safe to use `ref.listen` during the build phase; listeners are automatically cleaned up when the provider is recomputed.
8. Use the return value of `ref.listen` to manually remove listeners when needed.
9. Use `ref.read` only when you cannot use `ref.watch`, such as inside Notifier methods; `ref.read` does not listen to provider changes.
10. Be cautious with `ref.read`, as providers not being listened to may destroy their state if not actively watched.

### Auto Dispose & State Disposal
1. By default, with code generation, provider state is destroyed when the provider stops being listened to for a full frame.
2. Opt out of automatic disposal by setting `keepAlive: true` (codegen) or using `ref.keepAlive()` (manual).
3. When not using code generation, state is not destroyed by default; enable `.autoDispose` on providers to activate automatic disposal.
4. Always enable automatic disposal for providers that receive parameters to prevent memory leaks from unused parameter combinations.
5. State is always destroyed when a provider is recomputed, regardless of auto dispose settings.
6. Use `ref.onDispose` to register cleanup logic that runs when provider state is destroyed; do not trigger side effects or modify providers inside `onDispose`.
7. Use `ref.onCancel` to react when the last listener is removed, and `ref.onResume` when a new listener is added after cancellation.
8. Call `ref.onDispose` multiple times if neededβ€”once per disposable objectβ€”to ensure all resources are cleaned up.
9. Use `ref.invalidate` to manually force the destruction of a provider's state; if the provider is still listened to, a new state will be created.
10. Use `ref.invalidateSelf` inside a provider to force its own destruction and immediate recreation.
11. When invalidating parameterized providers, you can invalidate a specific parameter or all parameter combinations.
12. Use `ref.keepAlive` for fine-tuned control over state disposal; revert to automatic disposal using the return value of `ref.keepAlive`.
13. To keep provider state alive for a specific duration, combine a `Timer` with `ref.keepAlive` and dispose after the timer completes.
14. Consider using `ref.onCancel` and `ref.onResume` to implement custom disposal strategies, such as delayed disposal after a provider is no longer listened to.

### Eager Initialization
1. Providers are initialized lazily by default; they are only created when first used.
2. There is no built-in way to mark a provider for eager initialization due to Dart's tree shaking.
3. To eagerly initialize a provider, explicitly read or watch it at the root of your application (e.g., in a `Consumer` placed directly under `ProviderScope`).
4. Place the eager initialization logic in a public widget (such as `MyApp`) rather than in `main()` to ensure consistent test behavior.
5. Eagerly initializing a provider in a dedicated widget will not cause your entire app to rebuild when the provider changes; only the initialization widget will rebuild.
6. Handle loading and error states for eagerly initialized providers as you would in any `Consumer`, e.g., by returning a loading indicator or error widget.
7. Use `AsyncValue.requireValue` in widgets to read the data directly and throw a clear exception if the value is not ready, instead of handling loading/error states everywhere.
8. Avoid creating multiple providers or using overrides solely to hide loading/error states; this adds unnecessary complexity and is discouraged.

### First Provider & Network Requests
1. Always wrap your app with `ProviderScope` at the root (directly in `runApp`) to enable Riverpod for the entire application.
2. Place business logic such as network requests inside providers; use `Provider`, `FutureProvider`, or `StreamProvider` depending on the return type.
3. Providers are lazyβ€”network requests or logic inside a provider are only executed when the provider is first read.
4. Define provider variables as `final` and at the top level (global scope).
5. Use code generators like Freezed or json_serializable for models and JSON parsing to reduce boilerplate.
6. Use `Consumer` or `ConsumerWidget` in your UI to access providers via a `ref` object.
7. Handle loading and error states in the UI by using the `AsyncValue` API returned by `FutureProvider` and `StreamProvider`.
8. Multiple widgets can listen to the same provider; the provider will only execute once and cache the result.
9. Use `ConsumerWidget` or `ConsumerStatefulWidget` to reduce code indentation and improve readability over using a `Consumer` widget inside a regular widget.
10. To use both hooks and providers in the same widget, use `HookConsumerWidget` or `StatefulHookConsumerWidget` from `flutter_hooks` and `hooks_riverpod`.
11. Always install and use `riverpod_lint` to enable IDE refactoring and enforce best practices.
12. Do not put `ProviderScope` inside `MyApp`; it must be the top-level widget passed to `runApp`.
13. When handling network requests, always render loading and error states gracefully in the UI.
14. Do not re-execute network requests on widget rebuilds; Riverpod ensures the provider is only executed once unless explicitly invalidated.

### Passing Arguments to Providers
1. Use provider "families" to pass arguments to providers; add `.family` after the provider type and specify the argument type.
2. When using code generation, add parameters directly to the annotated function (excluding `ref`).
3. Always enable `autoDispose` for providers that receive parameters to avoid memory leaks.
4. When consuming a provider that takes arguments, call it as a function with the desired parameters (e.g., `ref.watch(myProvider(param))`).
5. You can listen to the same provider with different arguments simultaneously; each argument combination is cached separately.
6. The equality (`==`) of provider parameters determines cachingβ€”ensure parameters have consistent and correct equality semantics.
7. Avoid passing objects that do not override `==` (such as plain `List` or `Map`) as provider parameters; use `const` collections, custom classes with proper equality, or Dart 3 records.
8. Use the `provider_parameters` lint rule from `riverpod_lint` to catch mistakes with parameter equality.
9. For multiple parameters, prefer code generation or Dart 3 records, as records naturally override `==` and are convenient for grouping arguments.
10. If two widgets consume the same provider with the same parameters, only one computation/network request is made; with different parameters, each is cached separately.

### FAQ & Best Practices
1. Use `ref.refresh(provider)` when you want to both invalidate a provider and immediately read its new value; use `ref.invalidate(provider)` if you only want to invalidate without reading the value.
2. Always use the return value of `ref.refresh`; ignoring it will trigger a lint warning.
3. If a provider is invalidated while not being listened to, it will not update until it is listened to again.
4. Do not try to share logic between `Ref` and `WidgetRef`; move shared logic into a `Notifier` and call methods on the notifier via `ref.read(yourNotifierProvider.notifier).yourMethod()`.
5. Prefer `Ref` for business logic and avoid relying on `WidgetRef`, which ties logic to the UI layer.
6. Extend `ConsumerWidget` instead of using raw `StatelessWidget` when you need access to providers in the widget tree, due to limitations of `InheritedWidget`.
7. `InheritedWidget` cannot implement a reliable "on change" listener or track when widgets stop listening, which is required for Riverpod's advanced features.
8. Do not expect to reset all providers at once; instead, make providers that should reset depend on a "user" or "session" provider and reset that dependency.
9. `hooks_riverpod` and `flutter_hooks` are versioned independently; always add both as dependencies if using hooks.
10. Riverpod uses `identical` instead of `==` to filter updates for performance reasons, especially with code-generated models; override `updateShouldNotify` on Notifiers to change this behavior.
11. If you encounter "Using `ref` when a widget is about to or has been unmounted is unsafe" after the widget was disposed, ensure you check `context.mounted` before using `ref` after an `await` in an async callback.

### Provider Observers (Logging & Error Reporting)
1. Use a `ProviderObserver` to listen to all events in the provider tree for logging, analytics, or error reporting.
2. Extend the `ProviderObserver` class and override its methods to respond to provider lifecycle events:
   - `didAddProvider(ProviderObserverContext context, Object? value)`: called when a provider is added to the tree.
   - `didUpdateProvider(ProviderObserverContext context, Object? previousValue, Object? newValue)`: called when a provider is updated.
   - `didDisposeProvider(ProviderObserverContext context)`: called when a provider is disposed.
   - `providerDidFail(ProviderObserverContext context, Object error, StackTrace stackTrace)`: called when a synchronous provider throws an error.
3. Register your observer(s) by passing them to the `observers` parameter of `ProviderScope` (for Flutter apps) or `ProviderContainer` (for pure Dart).
4. You can register multiple observers if needed by providing a list to the `observers` parameter.
5. Use observers to integrate with remote error reporting services, log provider state changes, or trigger custom analytics.

### Performing Side Effects
1. Use Notifiers (`Notifier`, `AsyncNotifier`, etc.) to expose methods for performing side effects (e.g., POST, PUT, DELETE) and modifying provider state.
2. Always define provider variables as `final` and at the top level (global scope).
3. Choose the provider type (`NotifierProvider`, `AsyncNotifierProvider`, etc.) based on the return type of your logic.
4. Use provider modifiers like `autoDispose` and `family` as needed for cache management and parameterization.
5. Expose public methods on Notifiers for UI to trigger state changes or side effects.
6. In UI event handlers (e.g., button `onPressed`), use `ref.read` to call Notifier methods; avoid using `ref.watch` for imperative actions.
7. After performing a side effect, update the UI state by:
   - Setting the new state directly if the server returns the updated data.
   - Calling `ref.invalidateSelf()` to refresh the provider and re-fetch data.
   - Manually updating the local cache if the server does not return the new state.
8. When updating the local cache, prefer immutable state, but mutable state is possible if necessary.
9. Always handle loading and error states in the UI when performing side effects.
10. Use progress indicators and error messages to provide feedback for pending or failed operations.
11. Be aware of the pros and cons of each update approach:
    - Direct state update: most up-to-date but depends on server implementation.
    - Invalidate and refetch: always consistent with server, but may incur extra network requests.
    - Manual cache update: efficient, but risks state divergence from server.
12. Use hooks (`flutter_hooks`) or `StatefulWidget` to manage local state (e.g., pending futures) for showing spinners or error UI during side effects.
13. Do not perform side effects directly inside provider constructors or build methods; expose them via Notifier methods and invoke from the UI layer.

### Testing Providers
1. Always create a new `ProviderContainer` (unit tests) or `ProviderScope` (widget tests) for each test to avoid shared state between tests. Use a utility like `createContainer()` to set up and automatically dispose containers (see `/references/riverpod/testing/create_container.dart`).
2. In unit tests, never share `ProviderContainer` instances between tests. Example:
   ```dart
   final container = createContainer();
   expect(container.read(provider), equals('some value'));
   ```
3. In widget tests, always wrap your widget tree with `ProviderScope` when using `tester.pumpWidget`. Example:
   ```dart
   await tester.pumpWidget(
     const ProviderScope(child: YourWidgetYouWantToTest()),
   );
   ```
4. Obtain a `ProviderContainer` in widget tests using `ProviderScope.containerOf(BuildContext)`. Example:
   ```dart
   final element = tester.element(find.byType(YourWidgetYouWantToTest));
   final container = ProviderScope.containerOf(element);
   ```
5. After obtaining the container, you can read or interact with providers as needed for assertions. Example:
   ```dart
   expect(container.read(provider), 'some value');
   ```
6. For providers with `autoDispose`, prefer `container.listen` over `container.read` to prevent the provider's state from being disposed during the test.
7. Use `container.read` to read provider values and `container.listen` to listen to provider changes in tests.
8. Use the `overrides` parameter on `ProviderScope` or `ProviderContainer` to inject mocks or fakes for providers in your tests.
9. Use `container.listen` to spy on changes in a provider for assertions or to combine with mocking libraries.
10. Await asynchronous providers in tests by reading the `.future` property (for `FutureProvider`) or listening to streams.
11. Prefer mocking dependencies (such as repositories) used by Notifiers rather than mocking Notifiers directly.
12. If you must mock a Notifier, subclass the original Notifier base class instead of using `implements` or `with Mock`.
13. Place Notifier mocks in the same file as the Notifier being mocked if code generation is used, to access generated classes.
14. Use the `overrides` parameter to swap out Notifiers or providers for mocks or fakes in tests.
15. Keep all test-specific setup and teardown logic inside the test body or test utility functions. Avoid global state.
16. Ensure your test environment closely matches your production environment for reliable results.


```

### rules/provider.md

```markdown
### Provider Rules

1. Use `Provider`, `ChangeNotifierProvider`, `FutureProvider`, and `StreamProvider` to expose values and manage state in the widget tree.
2. Always specify the generic type when using `Provider`, `Consumer`, `context.watch`, `context.read`, or `context.select` for type safety.
```dart
final value = context.watch<int>();
```
3. Use `ChangeNotifierProvider` to automatically dispose of the model when it is no longer needed.
```dart
ChangeNotifierProvider(
  create: (_) => MyNotifier(),
  child: MyApp(),
)
```
4. For objects that depend on other providers or values that may change, use `ProxyProvider` or `ChangeNotifierProxyProvider` instead of creating the object from variables that can change over time.
```dart
ProxyProvider0(
  update: (_, __) => MyModel(count),
  child: ...
)
```
5. Use `MultiProvider` to group multiple providers and avoid deeply nested provider trees.
```dart
MultiProvider(
  providers: [
    Provider<Something>(create: (_) => Something()),
    Provider<SomethingElse>(create: (_) => SomethingElse()),
  ],
  child: someWidget,
)
```
6. Use `context.watch<T>()` to listen to changes and rebuild the widget when `T` changes.
7. Use `context.read<T>()` to access a provider without listening for changes (e.g., in callbacks).
8. Use `context.select<T, R>(R selector(T value))` to listen to only a small part of `T` and optimize rebuilds.
```dart
final selected = context.select<MyModel, int>((model) => model.count);
```
9. Use `Consumer<T>` or `Selector<T, R>` widgets for fine-grained rebuilds when you cannot access a descendant `BuildContext`.
```dart
Consumer<MyModel>(
  builder: (context, value, child) => Text('$value'),
)
```
10. To migrate from `ValueListenableProvider`, use `Provider` with `ValueListenableBuilder`.
```dart
ValueListenableBuilder<int>(
  valueListenable: myValueListenable,
  builder: (context, value, _) {
    return Provider<int>.value(
      value: value,
      child: MyApp(),
    );
  }
)
```
11. Do not create your provider’s object from variables that can change over time; otherwise, the object will not update when the value changes.
12. For debugging, implement `toString` or use `DiagnosticableTreeMixin` to improve how your objects appear in Flutter DevTools.
```dart
class MyClass with DiagnosticableTreeMixin {
  // ...
  @override
  String toString() => '$runtimeType(a: $a, b: $b)';
}
```
13. Do not attempt to obtain providers inside `initState` or `constructor`; use them in `build`, callbacks, or lifecycle methods where the widget is fully mounted.
14. You can use any object as state, not just `ChangeNotifier`; use `Provider.value()` with a `StatefulWidget` if needed.
15. If you have a very large number of providers (e.g., 150+), consider mounting them over time (e.g., during splash screen animation) or avoid `MultiProvider` to prevent StackOverflowError.


```

### rules/flutter_change_notifier.md

```markdown
### Flutter ChangeNotifier State Management Rules

1. Place shared state above the widgets that use it in the widget tree to enable proper rebuilds and avoid imperative UI updates.
2. Avoid directly mutating widgets or calling methods on them to change state; instead, rebuild widgets with new data.
3. Use a model class that extends `ChangeNotifier` to manage and notify listeners of state changes.
```dart
class CartModel extends ChangeNotifier {
  final List<Item> _items = [];
  UnmodifiableListView<Item> get items => UnmodifiableListView(_items);

  void add(Item item) {
    _items.add(item);
    notifyListeners();
  }
}
```
4. Keep internal state private within the model and expose unmodifiable views to the UI.
5. Call `notifyListeners()` in your model whenever the state changes to trigger UI rebuilds.
6. Use `ChangeNotifierProvider` to provide your model to the widget subtree that needs access to it.
```dart
ChangeNotifierProvider(
  create: (context) => CartModel(),
  child: MyApp(),
)
```
7. Wrap widgets that depend on the model’s state in a `Consumer<T>` widget to rebuild only when relevant data changes.
```dart
return Consumer<CartModel>(
  builder: (context, cart, child) => Stack(
    children: [
      child ?? const SizedBox.shrink(),
      Text('Total price: ${cart.totalPrice}'),
    ],
  ),
  child: const SomeExpensiveWidget(),
);
```
8. Always specify the generic type `<T>` for `Consumer<T>` and `Provider.of<T>` to ensure type safety and correct behavior.
9. Use the `child` parameter of `Consumer` to optimize performance by preventing unnecessary rebuilds of widgets that do not depend on the model.
10. Place `Consumer` widgets as deep in the widget tree as possible to minimize the scope of rebuilds.
```dart
return HumongousWidget(
  child: AnotherMonstrousWidget(
    child: Consumer<CartModel>(
      builder: (context, cart, child) {
        return Text('Total price: \${cart.totalPrice}');
      },
    ),
  ),
);
```
11. Do not wrap large widget subtrees in a `Consumer` if only a small part depends on the model; instead, wrap only the part that needs to rebuild.
12. Use `Provider.of<T>(context, listen: false)` when you need to access the model for actions (such as calling methods) but do not want the widget to rebuild on state changes.
```dart
Provider.of<CartModel>(context, listen: false).removeAll();
```
13. `ChangeNotifierProvider` automatically disposes of the model when it is no longer needed.
14. Use `MultiProvider` when you need to provide multiple models to the widget tree.
15. Write unit tests for your `ChangeNotifier` models to verify state changes and notifications.
16. Avoid rebuilding widgets unnecessarily; optimize rebuilds by structuring your widget tree and provider usage carefully.


```

### rules/mocktail.md

```markdown
### Mocktail Rules

1. Use a `Fake` when you need a lightweight, custom implementation of a class for testing, especially if you only need to override a subset of methods or provide specific behavior.
2. Use a `Mock` when you need to verify interactions (method calls, arguments, call counts) or need to stub method responses dynamically during tests.
3. Use `registerFallbackValue` to register a default value for a type that is used as an argument in a mock method, especially when the type is not nullable and is required for argument matching (e.g., `registerFallbackValue(MyCustomEvent())`).
4. Extend `Mock` to create a mock class for the class or interface you want to mock.
5. Use `when(() => mock.method()).thenReturn(value)` to stub method calls, and `thenThrow(error)` to stub errors.
6. Use `when(() => mock.method()).thenAnswer((invocation) => value)` for dynamic responses.
7. Use `verify(() => mock.method())` to check if a method was called; use `verifyNever(() => mock.method())` to check it was never called.
8. Use `verify(() => mock.method()).called(n)` to check the exact number of invocations.
9. Use argument matchers like `any()`, `captureAny()`, and `captureThat()` for flexible verification and stubbing.
10. Always register fallback values for custom types used with argument matchers before using them in stubs or verifications.
11. Prefer using real objects over mocks when possible; if not, use a tested fake implementation (`extends Fake`) over a mock.
12. Never add implementation or `@override` methods to a class extending `Mock`.
13. Only use mocks if your test asserts on interactions (calls to `verify`); otherwise, prefer real or fake objects.
14. Always stub async methods (returning `Future` or `Future<void>`) with `thenAnswer((_) async {})` or `thenReturn(Future.value(...))`.
15. Always include all named parameters in both `when` and `verify` calls, even if you only care about one. Use `any(named: 'paramName')` for those you don't care about.
16. If a method has default values for named parameters, Mocktail still expects all of them to be matched in both stubs and verifies.
17. Use `any()` for positional parameters in `when`/`verify` if you don't care about the exact instance.
18. Register fallback values for any custom types that are used with argument matchers before using them in your tests.
19. Stub every method you expect to be called, even if it's not the focus of your test, to prevent runtime errors.
20. When matching string outputs, make sure you understand what `.toString()` returns for the type you are using.


```

### rules/mockito.md

```markdown
### Mockito Rules

1. Use a `Fake` when you want a lightweight, custom implementation of a class for testing, especially when you only need to override a subset of methods or provide specific behavior for certain methods.
2. Use a `Mock` when you need to verify interactions (method calls, arguments, call counts) or when you need to stub method responses dynamically during tests.
3. Use `@GenerateMocks([YourClass])` or `@GenerateNiceMocks([MockSpec<YourClass>()])` to generate mock classes for your real classes.
4. Run `flutter pub run build_runner build` or `dart run build_runner build` after adding mock annotations to generate the mock files.
5. Only annotate files under `test/` for mock generation by default; use a `build.yaml` if you need to generate mocks elsewhere.
6. Create mock instances from generated classes (e.g., `var mock = MockCat();`).
7. Use `when(mock.method()).thenReturn(value)` to stub method calls, and `when(mock.method()).thenThrow(error)` to stub errors.
8. Use `thenAnswer` to calculate a response at call time: `when(mock.method()).thenAnswer((_) => value);`.
9. Use `thenReturnInOrder([v1, v2])` to return values in sequence for multiple calls.
10. Always stub methods or getters before interacting with them if you need specific return values.
11. Use `verify(mock.method())` to check if a method was called; use `verifyNever` to check it was never called.
12. Use `verify(mock.method()).called(n)` to check the exact number of invocations.
13. Use argument matchers like `any`, `argThat`, `captureAny`, and `captureThat` for flexible verification and stubbing.
14. Do not use `null` as an argument adjacent to an argument matcher.
15. For named arguments, use `any` or `argThat` as values, not as argument names (e.g., `when(mock.method(any, namedArg: any))`).
16. Use `captureAny` and `captureThat` to capture arguments passed to mocks for later assertions.
17. Use `untilCalled(mock.method())` to wait for an interaction in async tests.
18. Understand missing stub behavior: mocks generated with `@GenerateMocks` throw on missing stubs; those with `@GenerateNiceMocks` return a simple legal value.
19. To mock function types, define an abstract class with the required method signatures and generate mocks for it.
20. Prefer using real objects over mocks when possible; if not, use a tested fake implementation (`extends Fake`) over a mock.
21. Never stub out responses in a mock's constructor or within the mock class itself; always stub in your tests.
22. Never add implementation or `@override` methods to a class extending `Mock`.
23. Use `reset(mock)` to clear all stubs and interactions; use `clearInteractions(mock)` to clear only interactions.
24. Use `logInvocations([mock1, mock2])` to print all collected invocations for debugging.
25. Use `throwOnMissingStub(mock)` to throw if a mock method is called without a matching stub.
26. Data models should not be mocked if they can be constructed with stubbed data.
27. Only use mocks if your test asserts on interactions (calls to `verify`); otherwise, prefer real or fake objects.


```

### rules/testing.md

```markdown
## Testing
- When you look at a test, ask yourself: "Can this test actually fail if the real code is broken?" Or is it just passing because it's testing fake/mocked behavior that doesn’t reflect the real logic?
- Avoid writing tests that just confirm behavior guaranteed by the language, the standard library, or obvious code that can't really fail unless the environment is broken.
- Always use group() in test files β€” even if there’s only one test β€” and name the group after the class under test.
- Name test cases using β€œshould” to clearly describe the expected behavior. Example: test('value should start at 0', () {...}.


```

### rules/navigation.md

```markdown
# Navigation Rules (GoRouter)

### Core Principles
1. Use **GoRouter** for all navigation requirements in Flutter applications.
2. Adopt a **declarative** routing approach where the UI reflects the current route state.
3. Centralize route definitions in a dedicated router configuration file (e.g., `app_router.dart`).
4. Avoid using `Navigator.of(context)` directly unless handling simple dialogs or bottom sheets.
5. Use `context.go()` for switching screens (replacing the stack) and `context.push()` for stacking screens (preserving history).

### Setup & Configuration
1. Define a top-level or static `GoRouter` configuration.
2. Use `MaterialApp.router` or `CupertinoApp.router` in your `main.dart`.
3. Set the `initialLocation` to your app's entry point (e.g., `/` or `/home`).
4. Define routes using `GoRoute` objects with unique `path` and `name` properties.
5. Use `builder` to return the widget for a specific route.

```dart
final router = GoRouter(
  initialLocation: '/',
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomeScreen(),
    ),
  ],
);
```

### Navigation Actions
1. Prefer **named routes** for navigation to avoid hardcoding paths.
   - Use: `context.goNamed('profile')`
   - Avoid: `context.go('/profile')` (unless simple)
2. Use `context.pop()` to return to the previous screen.
3. Use `context.replace()` to replace the current route without animating.

### Parameter Handling
1. **Path Parameters**: Use `:paramName` in the route path for required parameters.
   - Definition: `path: '/user/:id'`
   - Access: `state.pathParameters['id']`
2. **Query Parameters**: Use `?key=value` for optional parameters.
   - Access: `state.uri.queryParameters['filter']`
3. **Extra Object**: Use `extra` for passing complex objects (non-primitive).
   - Sending: `context.goNamed('details', extra: userObject)`
   - Access: `state.extra as User`
   - **Warning**: `extra` data is not persisted on web refresh; handle nulls gracefully.

### Redirection & Guards
1. Use `redirect` to protect routes (e.g., authentication checks).
2. Implement redirection logic at the top-level `GoRouter` or individual `GoRoute`.
3. Return `null` to allow navigation, or a path string to redirect.
4. Combine with a `Listenable` (like a specific auth provider) in `refreshListenable` to trigger redirects automatically on state changes.

```dart
redirect: (context, state) {
  final isLoggedIn = authService.isLoggedIn;
  final isLoggingIn = state.uri.toString() == '/login';

  if (!isLoggedIn && !isLoggingIn) return '/login';
  if (isLoggedIn && isLoggingIn) return '/';
  return null;
},
```

### Nested Navigation
1. Use `ShellRoute` or `StatefulShellRoute` for persistent bottom navigation bars or side menus.
2. `ShellRoute` rebuilds the shell when switching branches.
3. `StatefulShellRoute` preserves the state of each branch (recommended for bottom tabs).

### Error Handling
1. Provide an `errorBuilder` to show a custom 404/Error page when a route is not found.

### Type Safety
1. Consider using packages like `go_router_builder` for type-safe routes if the project scale justifies the extra code generation setup.

```

### rules/effective_dart.md

```markdown
# Effective Dart Rules

### Naming Conventions
1. Use terms consistently throughout your code.
2. Follow existing mnemonic conventions when naming type parameters (e.g., `E` for element, `K`/`V` for key/value, `T`/`S`/`U` for generic types).
3. Name types using `UpperCamelCase` (classes, enums, typedefs, type parameters).
4. Name extensions using `UpperCamelCase`.
5. Name packages, directories, and source files using `lowercase_with_underscores`.
6. Name import prefixes using `lowercase_with_underscores`.
7. Name other identifiers using `lowerCamelCase` (variables, parameters, named parameters).
8. Capitalize acronyms and abbreviations longer than two letters like words.
9. Avoid abbreviations unless the abbreviation is more common than the unabbreviated term.
10. Prefer putting the most descriptive noun last in names.
11. Consider making code read like a sentence when designing APIs.
12. Prefer a noun phrase for non-boolean properties or variables.
13. Prefer a non-imperative verb phrase for boolean properties or variables.
14. Prefer the positive form for boolean property and variable names.
15. Consider omitting the verb for named boolean parameters.
16. Use camelCase for variable and function names.
17. Use PascalCase for class names.
18. Use snake_case for file names.

### Types and Functions
1. Use class modifiers to control if your class can be extended or used as an interface.
2. Type annotate variables without initializers.
3. Type annotate fields and top-level variables if the type isn't obvious.
4. Annotate return types on function declarations.
5. Annotate parameter types on function declarations.
6. Write type arguments on generic invocations that aren't inferred.
7. Annotate with `dynamic` instead of letting inference fail.
8. Use `Future<void>` as the return type of asynchronous members that do not produce values.
9. Use getters for operations that conceptually access properties.
10. Use setters for operations that conceptually change properties.
11. Use a function declaration to bind a function to a name.
12. Use inclusive start and exclusive end parameters to accept a range.

### Style
1. Format your code using `dart format`.
2. Use curly braces for all flow control statements.
3. Prefer `final` over `var` when variable values won't change.
4. Use `const` for compile-time constants.
5. Prefer lines 80 characters or fewer for readability.

### Imports & Files
1. Don't import libraries inside the `src` directory of another package.
2. Don't allow import paths to reach into or out of `lib`.
3. Prefer relative import paths within a package.
4. Don't use `/lib/` or `../` in import paths.
5. Consider writing a library-level doc comment for library files.

### Structure
1. Keep files focused on a single responsibility.
2. Limit file length to maintain readability.
3. Group related functionality together.
4. Prefer making fields and top-level variables `final`.
5. Consider making your constructor `const` if the class supports it.
6. Prefer making declarations private.

### Usage
1. Use strings in `part of` directives.
2. Use adjacent strings to concatenate string literals.
3. Use collection literals when possible.
4. Use `whereType()` to filter a collection by type.
5. Test for `Future<T>` when disambiguating a `FutureOr<T>` whose type argument could be `Object`.
6. Follow a consistent rule for `var` and `final` on local variables.
7. Initialize fields at their declaration when possible.
8. Use initializing formals when possible.
9. Use `;` instead of `{}` for empty constructor bodies.
10. Use `rethrow` to rethrow a caught exception.
11. Override `hashCode` if you override `==`.
12. Make your `==` operator obey the mathematical rules of equality.
13. Prefer specific exception handling: avoid generic `catch (e)` handlers when possible; use `on SomeException catch (e, _)` and `.onError<T>` (or specific `on` clauses) instead of broad `.catchError`/catch blocks.

### Documentation
1. Format comments like sentences.
2. Use `///` doc comments to document members and types; don't use block comments for documentation.
3. Prefer writing doc comments for public APIs.
4. Consider writing doc comments for private APIs.
5. Consider including explanations of terminology, links, and references in library-level docs.
6. Start doc comments with a single-sentence summary.
7. Separate the first sentence of a doc comment into its own paragraph.
8. Avoid redundancy with the surrounding context.
9. Prefer starting comments of a function or method with third-person verbs if its main purpose is a side effect.
10. Prefer a noun phrase or non-imperative verb phrase for a function or method if returning a value is its primary purpose.
11. Prefer starting non-boolean variable or property comments with a noun phrase.
12. Prefer starting boolean variable or property comments with "Whether" followed by a noun or gerund phrase.
13. Prefer starting library or type comments with noun phrases.
14. Use square brackets in doc comments to refer to in-scope identifiers.
15. Use prose to explain parameters, return values, and exceptions.
	- Consider using short sections such as "The [parameter]", "Returns", and "Throws" to make parameter and error documentation clearer.
16. Put doc comments before metadata annotations.
17. Document why code exists or how it should be used, not just what it does.

### Testing
1. Write unit tests for business logic.
2. Write widget tests for UI components.
3. Aim for good test coverage.

### Widgets
1. Extract reusable widgets into separate components.
2. Use `StatelessWidget` when possible.
3. Keep build methods simple and focused.

### State Management
1. Choose appropriate state management based on complexity.
2. Avoid unnecessary `StatefulWidget`s.
3. Keep state as local as possible.

### Performance
1. Use `const` constructors when possible.
2. Avoid expensive operations in build methods.
3. Implement pagination for large lists.


```

### rules/dart_3_updates.md

```markdown
# Dart 3 Updates

### Branches
1. Use `if` statements for conditional branching. The condition must evaluate to a boolean.
2. `if` statements support optional `else` and `else if` clauses for multiple branches.
3. Use `if-case` statements to match and destructure a value against a single pattern. Example: `if (pair case [int x, int y]) { ... }`
4. If the pattern in an `if-case` matches, variables defined in the pattern are in scope for that branch.
5. If the pattern does not match in an `if-case`, control flows to the `else` branch if present.
6. Use `switch` statements to match a value against multiple patterns (cases). Each `case` can use any kind of pattern.
7. When a value matches a `case` pattern in a `switch` statement, the case body executes and control jumps to the end of the switch. `break` is not required.
8. You can end a non-empty `case` clause with `continue`, `throw`, or `return`.
9. Use `default` or `_` in a `switch` statement to handle unmatched values.
10. Empty `case` clauses fall through to the next case. Use `break` to prevent fallthrough.
11. Use `continue` with a label for non-sequential fallthrough between cases.
12. Use logical-or patterns (e.g., `case a || b`) to share a body or guard between cases.
13. Use `switch` expressions to produce a value based on matching cases. Syntax differs from statements: omit `case`, use `=>` for bodies, and separate cases with commas.
14. In `switch` expressions, the default case must use `_` (not `default`).
15. Dart checks for exhaustiveness in `switch` statements and expressions, reporting a compile-time error if not all possible values are handled.
16. To ensure exhaustiveness, use a default (`default` or `_`) case, or switch over enums or sealed types.
17. Use the `sealed` modifier on a class to enable exhaustiveness checking when switching over its subtypes.
18. Add a guard clause to a `case` using `when` to further constrain when a case matches. Example: `case pattern when condition:`
19. Guard clauses can be used in `if-case`, `switch` statements, and `switch` expressions. The guard is evaluated after pattern matching.
20. If a guard clause evaluates to false, execution proceeds to the next case (does not exit the switch).

### Patterns
1. Patterns are a syntactic category that represent the shape of values for matching and destructuring.
2. Pattern matching checks if a value has a certain shape, constant, equality, or type.
3. Pattern destructuring allows extracting parts of a matched value and binding them to variables.
4. Patterns can be nested, using subpatterns (outer/inner patterns) for recursive matching and destructuring.
5. Use wildcard patterns (`_`) to ignore parts of a matched value; use rest elements in list patterns to ignore remaining elements.
6. Patterns can be used in:
   - Local variable declarations and assignments
   - For and for-in loops
   - If-case and switch-case statements
   - Control flow in collection literals
7. Pattern variable declarations start with `var` or `final` and bind new variables from the matched value. Example: `var (a, [b, c]) = ('str', [1, 2]);`
8. Pattern variable assignments destructure a value and assign to existing variables. Example: `(b, a) = (a, b); // swap values`
9. Every case clause in `switch` and `if-case` contains a pattern. Any kind of pattern can be used in a case.
10. Case patterns are refutable; if the pattern doesn't match, execution continues to the next case.
11. Destructured values in a case become local variables scoped to the case body.
12. Use logical-or patterns (e.g., `case a || b`) to match multiple alternatives in a single case.
13. Use logical-or patterns with guards (`when`) to share a body or guard between cases.
14. Guard clauses (`when`) evaluate a condition after matching; if false, execution proceeds to the next case.
15. Patterns can be used in for and for-in loops to destructure collection elements (e.g., destructuring `MapEntry` in map iteration).
16. Object patterns match named object types and destructure their data using getters. Example: `var Foo(:one, :two) = myFoo;`
17. Use patterns to destructure records, including positional and named fields, directly into local variables.
18. Patterns enable algebraic data type style code: use sealed classes and switch on subtypes for exhaustive matching.
19. Patterns simplify validation and destructuring of complex data structures, such as JSON, in a declarative way. Example: `if (data case {'user': [String name, int age]}) { ... }`
20. Patterns provide a concise alternative to verbose type-checking and destructuring code.

### Pattern Types
1. Pattern precedence determines evaluation order; use parentheses to group lower-precedence patterns.
2. Logical-or patterns (`pattern1 || pattern2`) match if any branch matches, evaluated left-to-right. All branches must bind the same set of variables.
3. Logical-and patterns (`pattern1 && pattern2`) match if both subpatterns match. Bound variable names must not overlap between subpatterns.
4. Relational patterns (`==`, `!=`, `<`, `>`, `<=`, `>=`) match if the value compares as specified to a constant. Useful for numeric ranges and can be combined with logical-and.
5. Cast patterns (`subpattern as Type`) assert and cast a value to a type before passing it to a subpattern. Throws if the value is not of the type.
6. Null-check patterns (`subpattern?`) match if the value is not null, then match the inner pattern. Binds the non-nullable type. Use constant pattern `null` to match null.
7. Null-assert patterns (`subpattern!`) match if the value is not null, else throw. Use in variable declarations to eliminate nulls. Use constant pattern `null` to match null.
8. Constant patterns match if the value is equal to a constant (number, string, bool, named constant, const constructor, const collection, etc.). Use parentheses and `const` for complex expressions.
9. Variable patterns (`var name`, `final Type name`) bind new variables to matched/destructured values. Typed variable patterns only match if the value has the declared type.
10. Identifier patterns (`foo`, `_`) act as variable or constant patterns depending on context. `_` always acts as a wildcard and matches/discards any value.
11. Parenthesized patterns (`(subpattern)`) control pattern precedence and grouping, similar to expressions.
12. List patterns (`[subpattern1, subpattern2]`) match lists and destructure elements by position. The pattern length must match the list unless a rest element is used.
13. Rest elements (`...`, `...rest`) in list patterns match arbitrary-length lists or collect unmatched elements into a new list.
14. Map patterns (`{"key": subpattern}`) match maps and destructure by key. Only specified keys are matched; missing keys throw a `StateError`.
15. Record patterns (`(subpattern1, subpattern2)`, `(x: subpattern1, y: subpattern2)`) match records by shape and destructure positional/named fields. Field names can be omitted if inferred from variable or identifier patterns.
16. Object patterns (`ClassName(field1: subpattern1, field2: subpattern2)`) match objects by type and destructure using getters. Extra fields in the object are ignored.
17. Wildcard patterns (`_`, `Type _`) match any value without binding. Useful for ignoring values or type-checking without binding.
18. All pattern types can be nested and combined for expressive and precise matching and destructuring.

### Records
1. Records are anonymous, immutable, aggregate types that bundle multiple objects into a single value.
2. Records are fixed-sized, heterogeneous, and strongly typed. Each field can have a different type.
3. Records are real values: store them in variables, nest them, pass to/from functions, and use in lists, maps, and sets.
4. Record expressions use parentheses with comma-delimited positional and/or named fields, e.g. `('first', a: 2, b: true, 'last')`.
5. Record type annotations use parentheses with comma-delimited types. Named fields use curly braces: `({int a, bool b})`.
6. The names of named fields are part of the record's type (shape). Records with different named field names have different types.
7. Positional field names in type annotations are for documentation only and do not affect the record's type.
8. Record fields are accessed via built-in getters: positional fields as `$1`, `$2`, etc., and named fields by their name (e.g., `.a`).
9. Records are immutable: fields do not have setters.
10. Records are structurally typed: the set, types, and names of fields define the record's type (shape).
11. Two records are equal if they have the same shape and all corresponding field values are equal. Named field order does not affect equality.
12. Records automatically define `hashCode` and `==` based on structure and field values.
13. Use records for functions that return multiple values; destructure with pattern matching: `var (name, age) = userInfo(json);`
14. Destructure named fields with the colon syntax: `final (:name, :age) = userInfo(json);`
15. Using records for multiple returns is more concise and type-safe than using classes, lists, or maps.
16. Use lists of records for simple data tuples with the same shape.
17. Use type aliases (`typedef`) for record types to improve readability and maintainability.
18. Changing a record type alias does not guarantee all code using it is still type-safe; only classes provide full abstraction/encapsulation.
19. Extension types can wrap records but do not provide full abstraction or protection.
20. Records are best for simple, immutable data aggregation; use classes for abstraction, encapsulation, and behavior.


```

### rules/code_review.md

```markdown
# Code Review Ruleset for LLMs

1. Check that the current branch is a feature, bugfix, or PR/MR branch and not a main or develop branch.
2. Verify the branch is up-to-date with the target branch (main).
3. Identify the target branch for the merge and list all files that have changed, been added, or deleted.
4. For every change, look up the commit title and review how any connected components are implemented. 
5. Do not assume a change or fix is correct without investigating the implementation details.
6. Avoid complimenting or critiquing by default; be as objective and reasonable as possible. Instead of offering automatic praise, take a devil's advocate approach and give honest, thoughtful feedback.
7. If the change or fix remains difficult to understand after several attempts, include this in your report. 
8. Fetch online documentation if you're unsure about the best practices for a particular package or library.
9. For each changed file, check that the file is in the correct directory.
10. For each changed file, check that the file name follows naming conventions.
11. For each changed file, verify the file's responsibility is clear and that the reason for its change or addition is understandable.
12. For each changed file, review the code for readability and ensure variable, function, and class names are descriptive and consistent.
13. For each changed file, check the logic and correctness of the code, ensuring there are no logic errors or missing edge cases.
14. For each changed file, check that the code is modular and does not contain unnecessary duplication (maintainability).
15. For each changed file, ensure errors and exceptions are handled appropriately.
16. For each changed file, check for potential security concerns such as input validation and secrets in code.
17. For each changed file, check for obvious performance issues or inefficiencies.
18. For each changed file, verify that public APIs, complex logic, and new modules are documented.
19. For each changed file, ensure there is sufficient test coverage for new or changed logic.
20. For each changed file, ensure the code matches the project's style guide and coding patterns.
21. For generated files, confirm they are up-to-date and not manually edited.
22. Check that the overall change set is focused and scoped to the stated purpose and does not include unrelated or unnecessary changes.
23. Verify that the PR/MR description accurately reflects the changes made.
24. Ensure there are new or updated tests covering new or changed logic.
25. If there's a test, always evaluate whether the test could fail or if it's only checking a mock implementation.
26. Ensure all tests pass in the continuous integration system.
27. Provide clear, constructive feedback for any issues found, including suggestions for improvement and requests for clarification if anything is unclear.
28. The expected output is an answer in the chat, mentioning conclusions and recommendations per file.


```

### rules/flutter_app_architecture.md

```markdown
# Flutter App Architecture

### Architecture
1. Separate your features into a UI Layer (presentation), a Data Layer (business data and logic), and, for complex apps, consider adding a Domain (Logic) Layer between UI and Data layers to encapsulate business logic and use-cases.
2. You can organize code by feature: The classes needed for each feature are grouped together. For example, you might have an auth directory, which would contain files like auth_viewmodel.dart (or, depending on your state management approach: auth_controller.dart, auth_provider.dart, auth_bloc.dart), login_usecase.dart, logout_usecase.dart, login_screen.dart, logout_button.dart, etc. Alternatively, you can organize by type or use a hybrid approach.
3. Only allow communication between adjacent layers; the UI layer should not access the data layer directly, and vice versa.
4. Introduce a Logic (Domain) Layer only for complex business logic that does not fit cleanly in the UI or Data layers.
5. Clearly define the responsibilities, boundaries, and interfaces of each layer and component (Views, View Models, Repositories, Services).
6. Further divide each layer into components with specific responsibilities and well-defined interfaces.
7. In the UI Layer, use Views to describe how to present data to the user; keep logic minimal and only UI-related.
8. Pass events from Views to View Models in response to user interactions.
9. In View Models, contain logic to convert app data into UI state and maintain the current state needed by the view.
10. Expose callbacks (commands) from View Models to Views and retrieve/transform data from repositories.
11. In the Data Layer, use Repositories as the single source of truth (SSOT) for model data and to handle business logic such as caching, error handling, and refreshing data.
12. Only the SSOT class (usually the repository) should be able to mutate its data; all other classes should read from it.
13. Repositories should transform raw data from services into domain models and output data consumed by View Models.
14. Use Services to wrap API endpoints and expose asynchronous response objects; services should isolate data-loading and hold no state.
15. Use dependency injection to provide components with their dependencies, enabling testability and flexibility.

### Data Flow and State
1. Follow unidirectional data flow: state flows from the data layer through the logic layer to the UI layer, and events from user interaction flow in the opposite direction.
2. Data changes should always happen in the SSOT (data layer), not in the UI or logic layers.
3. The UI should always reflect the current (immutable) state; trigger UI rebuilds only in response to state changes.
4. Views should contain as little logic as possible and be driven by state from View Models.

### Use Cases / Interactors
1. Introduce use cases/interactors in the domain layer only when logic is complex, reused, or merges data from multiple repositories.
2. Use cases depend on repositories and may be used by multiple view models.
3. Add use cases only when needed; refactor to use use-cases exclusively if logic is repeatedly shared across view models.

### Extensibility and Testability
1. All architectural components should have well-defined inputs and outputs (interfaces).
2. Favor dependency injection to allow swapping implementations without changing consumers.
3. Test view models by mocking repositories; test UI logic independently of widgets.
4. Design components to be easily replaceable and independently testable.

### Best Practices
1. Strongly recommend following separation of concerns and layered architecture.
2. Strongly recommend using dependency injection for testability and flexibility.
3. Recommend using MVVM as the default pattern, but adapt as needed for your app's complexity.
4. Use key-value storage for simple data (e.g., configuration, preferences) and SQL storage for complex relationships.
5. Use optimistic updates to improve perceived responsiveness by updating the UI before operations complete.
6. Support offline-first strategies by combining local and remote data sources in repositories and enabling synchronization as appropriate.
7. Keep views focused on presentation and extract reusable widgets into separate components.
8. Use `StatelessWidget` when possible and avoid unnecessary `StatefulWidget`s.
9. Keep build methods simple and focused on rendering.
10. Choose state management approaches appropriate to the complexity of your app.
11. Keep state as local as possible to minimize rebuilds and complexity.
12. Use `const` constructors when possible to improve performance.
13. Avoid expensive operations in build methods and implement pagination for large lists.
14. Keep files focused on a single responsibility and limit file length for readability.
15. Group related functionality together and use `final` for fields and top-level variables when possible.
16. Prefer making declarations private and consider making constructors `const` if the class supports it.
17. Follow Dart naming conventions and format code using `dart format`.
18. Use curly braces for all flow control statements to ensure clarity and prevent bugs.
19. Prefer explicit typing and generics on public APIs (for example, prefer typed command signatures such as `Command0<void>` rather than untyped/dynamic signatures) to improve clarity and type safety.
20. For small immutable domain or data models, prefer using `abstract class` with `const` constructors and `final` fields where it improves readability and enforces immutability.
21. Use descriptive constant names for resources and table identifiers (for example prefer `_todoTableName` over compact prefixes like `_kTableTodo`) to improve clarity across examples and migrations.


```

### rules/json_serialization.md

```markdown
# JSON Serialization Rules

### Overview
1. Use **json_serializable** for all JSON parsing tasks to ensure type safety and compile-time checking.
2. Avoid manual serialization (`Map<String, dynamic>`) for domain models.
3. Use the `json_annotation` package for defining models and `build_runner` for code generation.

### Setup
1. Add dependencies in `pubspec.yaml`:
   ```yaml
   dependencies:
     json_annotation: ^latest

   dev_dependencies:
     build_runner: ^latest
     json_serializable: ^latest
   ```

### Model Structure
1. Create a class and annotate it with `@JsonSerializable()`.
2. Define the `fromJson` factory constructor.
3. Define the `toJson` method.
4. Include the `part` directive pointing to the generated file (`.g.dart`).

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

part 'user.g.dart';

@JsonSerializable()
class User {
  final String id;
  final String name;
  
  // Optional field
  final String? email;

  User({required this.id, required this.name, this.email});

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  
  Map<String, dynamic> toJson() => _$UserToJson(this);
}
```

### Configuration & Best Practices
1. **Explicit Nulls**: Use `includeIfNull: false` in `@JsonSerializable` if you don't want null values in the JSON output.
2. **Field Renaming**: Use `@JsonKey(name: 'api_field_name')` to map JSON keys that differ from Dart property names (e.g., snake_case to camelCase).
3. **Default Values**: Use `@JsonKey(defaultValue: 'value')` to provide fallbacks for missing keys.
4. **Ignoring Fields**: Use `@JsonKey(includeFromJson: false, includeToJson: false)` for properties that should not be serialized.
5. **Enums**: Annotate enums with `@JsonEnum()` for easier serialization.
   - Use `@JsonValue('value')` on enum members to map to specific JSON values.

### Code Generation
1. Run the builder once:
   ```bash
   dart run build_runner build
   ```
2. Run the builder in watch mode (recommended during development):
   ```bash
   dart run build_runner watch
   ```
3. Resolve conflicting outputs (if needed):
   ```bash
   dart run build_runner build --delete-conflicting-outputs
   ```

### Advanced Usage
1. **Custom Converters**: Use `JsonConverter<T, S>` for complex types that `json_serializable` doesn't handle natively (e.g., `DateTime` formats, custom value objects).
   ```dart
   class MyDateConverter implements JsonConverter<DateTime, int> {
     const MyDateConverter();
     // ... implement fromJson and toJson
   }
   
   @MyDateConverter()
   final DateTime createdAt;
   ```
2. **Generic Classes**: `json_serializable` supports generic classes, but requires extra handling for `fromJsonT` and `toJsonT` functions.

### VS Code Integration
1. Use snippets to generate the boilerplate code quickly.
2. Hide `.g.dart` files in the file explorer if they clutter the view (configure in `.vscode/settings.json`).

```

### rules/flutter_errors.md

```markdown
### Common Flutter Errors

1. If you get a "RenderFlex overflowed" error, check if a `Row` or `Column` contains unconstrained widgets. Fix by wrapping children in `Flexible`, `Expanded`, or by setting constraints.
2. If you get "Vertical viewport was given unbounded height", ensure `ListView` or similar scrollable widgets inside a `Column` have a bounded height (e.g., wrap with `Expanded` or `SizedBox`).
3. If you get "An InputDecorator...cannot have an unbounded width", constrain the width of widgets like `TextField` using `Expanded`, `SizedBox`, or by placing them in a parent with width constraints.
4. If you get a "setState called during build" error, do not call `setState` or `showDialog` directly inside the build method. Trigger dialogs or state changes in response to user actions or after the build completes (e.g., using `addPostFrameCallback`).
5. If you get "The ScrollController is attached to multiple scroll views", make sure each `ScrollController` is only attached to a single scrollable widget at a time.
6. If you get a "RenderBox was not laid out" error, check for missing or unbounded constraints in your widget tree. This is often caused by using widgets like `ListView` or `Column` without proper size constraints.
7. Use the Flutter Inspector and review widget constraints to debug layout issues. Refer to the official documentation on constraints if needed.


```