```
├── .DS_Store
├── LICENSE
├── README.md
├── combined/
├── README.md
├── flutter_dart.md
├── flutter_dart__under_6K.md
├── flutter_dart_bloc_mocktail.md
├── flutter_dart_bloc_mocktail__under_6K.md
├── flutter_dart_change_notifier.md
├── flutter_dart_change_notifier__under_6K.md
├── flutter_dart_provider.md
├── flutter_dart_provider__under_6K.md
├── flutter_dart_riverpod_mockito.md
├── flutter_dart_riverpod_mockito__under_6K.md
├── flutter_with_bloc.md
├── flutter_with_bloc__under_6K.md
├── flutter_with_riverpod.md
├── flutter_with_riverpod__under_6K.md
├── media/
├── flutter_ai_rules.png
├── mocktail_md_01.png
├── mocktail_md_02.png
├── rules/
├── bloc.md
├── dart_3_updates.md
├── effective_dart.md
├── firebase/
├── cloud_firestore.md
├── cloud_functions.md
├── firebase_analytics.md
├── firebase_app_check.md
├── firebase_auth.md
```
## /.DS_Store
Binary file available at https://raw.githubusercontent.com/evanca/flutter-ai-rules/refs/heads/main/.DS_Store
## /LICENSE
``` path="/LICENSE"
MIT License
Copyright (c) 2025 Ivanna
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
## /README.md
# Flutter Rules for Windsurf, Cursor, and Other AI-Powered IDEs
## ⚡ TLDR
If you want to use `.cursor/rules` or `.windsurfrules`, just copy the contents of the rule set of your choice (e.g., `combined/flutter_dart__under_6K.md`) into your IDE’s global or local rules.
For maximum control, you can also copy the `/rules` folder into your project and reference rules as needed (e.g., "Read @rules/firebase/ and set up a project with Realtime Database, App Check, and Analytics.").
## 🚀 Introduction
This repository provides a comprehensive, (almost) non-opinionated collection of Flutter-related rules tailored for use with **Windsurf**, **Cursor**, and other AI-powered IDEs. These rules are designed to improve your development workflow, ensure consistency, and help you get the most out of your AI coding assistant.
## 📁 Repository Structure
- **`rules/`**
Contains individual rule files, each focused on a specific topic or tool (e.g., `bloc.md`, `effective_dart.md`, etc.).
These files are:
- Based **only** on official documentation from Flutter, Dart, or relevant package websites.
- Categorized by subject to make them easy to mix, match, and reference.
- Meant to be refined, adjusted, or extracted based on your project needs.
- **`combined/`**
Contains pre-made, curated sets of rules that combine commonly used topics (e.g., Flutter + Riverpod + Mockito).
These files:
- Are kept under **6,000 characters** to comply with **Windsurf's** limit.
- Can be used **as-is** by copying them into your global or local rules configuration.
## ✅ How To Use
### Option 1: Use Pre-Made Combined Rules
If you want a quick setup:
1. Browse the [`combined/`](./combined) folder.
2. Copy a file that suits your project.
3. Paste it into your IDE's global or local rules config.
4. You're ready to go.
### Option 2: Use Individual Rule Files
If you prefer more control:
1. Browse the [`rules/`](./rules) folder.
2. Pick files relevant to your project (e.g., `riverpod.md`, `bloc.md`, etc.).
3. You can:
- **Include** them directly in your IDE setup.
- **Reference** them in prompts to add context.
- **Extract** only the parts that are useful for your context.
- **Include** them partially or fully in a PRD (Product Requirements Document).
Everything is modular — use what works best for you.
## 📏 No Opinions, Just Documentation
All rules are sourced from official documentation — no personal preferences or subjective interpretations. That’s intentional. You’re free to alter them to your taste, but this repo keeps things objective by sticking to the source.
Note: This might sometimes lead to contradictory rules (e.g., if one package suggests one folder architecture and another recommends a different one).
## 📌 Use Cases
- Set up global rules for a Flutter project in your IDE.
- Configure project-specific constraints for popular state management packages.
- Provide clear expectations in a PDR when working with a team.
- Extract only what you need to avoid rule clutter.
## 🛠️ Contributing
Contributions are welcome! If you'd like to suggest a new rule or improve an existing one, here’s how you can help:
1. Fork this repository.
2. Add or modify rules in the appropriate folder.
3. Submit a pull request with a clear explanation of your changes.
**Make sure to include an official documentation link** for any rule set you’re adding or modifying to keep everything objective and reliable.
## 📚 References
Here are the official sources that have been used to build these rules:
### Flutter
- [Flutter App Architecture](https://docs.flutter.dev/app-architecture) - Official Flutter architecture guidelines
- [Flutter Common Errors](https://docs.flutter.dev/testing/common-errors) - Common errors documentation
- [Flutter ChangeNotifier State Management](https://docs.flutter.dev/data-and-backend/state-mgmt/simple) - Simple state management with ChangeNotifier
### Dart
- [Effective Dart](https://dart.dev/effective-dart) - Official Dart style guidelines
- [Dart 3 Updates](https://dart.dev/language) - Documentation on Dart 3 features including:
- [Records](https://dart.dev/language/records)
- [Patterns](https://dart.dev/language/patterns)
- [Pattern Types](https://dart.dev/language/pattern-types)
- [Branches](https://dart.dev/language/branches)
### State Management
- [Bloc Library](https://bloclibrary.dev/) - Official Bloc library documentation
- [Provider](https://pub.dev/packages/provider) - Official Provider package documentation
- [Riverpod](https://riverpod.dev/) - Official Riverpod documentation
### Testing
- [Mockito](https://pub.dev/packages/mockito) - Official Mockito for Dart documentation
- [Mocktail](https://pub.dev/packages/mocktail) - Official Mocktail documentation
### Firebase
- [Firebase for Flutter](https://firebase.google.com/docs/flutter/setup) - Official Firebase Flutter documentation
- [Code with Andrea](https://codewithandrea.com/articles/flutter-firebase-multiple-flavors-flutterfire-cli/) - How to Setup Flutter & Firebase with Multiple Flavors using the FlutterFire CLI
## /combined/README.md
# Combined Rules Character Count
This document contains character counts for all combined rule files.
| File | Character Count |
|------|----------------|
| flutter_dart__under_6K.md | 5858 |
| flutter_dart_bloc_mocktail__under_6K.md | 4751 |
| flutter_dart_bloc_mocktail.md | 24801 |
| flutter_dart_change_notifier__under_6K.md | 5553 |
| flutter_dart_change_notifier.md | 17155 |
| flutter_dart_provider__under_6K.md | 5681 |
| flutter_dart_provider.md | 17264 |
| flutter_dart_riverpod_mockito__under_6K.md | 5635 |
| flutter_dart_riverpod_mockito.md | 31917 |
| flutter_dart.md | 14400 |
| flutter_with_bloc__under_6K.md | 5145 |
| flutter_with_bloc.md | 22250 |
| flutter_with_riverpod__under_6K.md | 5779 |
| flutter_with_riverpod.md | 30379 |
---
**Windsurf limits:** `global_rules.md` and `.windsurfrules` are limited to 6000 characters each. Any content above 6000 characters will be truncated and Cascade will not be aware of them.
Source: [Windsurf Documentation - Memories](https://docs.windsurf.com/windsurf/memories), accessed Apr 15 2025.
## /combined/flutter_dart.md
# 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` 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.
### 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` when disambiguating a `FutureOr` 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.
### 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. Use square brackets in doc comments to refer to in-scope identifiers.
9. Use prose to explain parameters, return values, and exceptions.
10. Put doc comments before metadata annotations.
11. 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.
# 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;`
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.
# 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.
## /combined/flutter_dart__under_6K.md
# Effective Dart Rules
### Naming Conventions
1. Use terms consistently throughout your code.
2. Name types using `UpperCamelCase` (classes, enums, typedefs, type parameters).
3. Name extensions using `UpperCamelCase`.
4. Name packages, directories, and source files using `lowercase_with_underscores`.
5. Name import prefixes using `lowercase_with_underscores`.
6. Name other identifiers using `lowerCamelCase` (variables, parameters, named parameters).
7. Capitalize acronyms and abbreviations longer than two letters like words.
8. Avoid abbreviations unless the abbreviation is more common than the unabbreviated term.
9. Prefer putting the most descriptive noun last in names.
10. Prefer a noun phrase for non-boolean properties or variables.
### Types and Functions
1. Use class modifiers to control if your class can be extended or used as an interface.
2. Type annotate fields and top-level variables if the type isn't obvious.
3. Annotate return types on function declarations.
4. Annotate parameter types on function declarations.
5. Use `Future` as the return type of asynchronous members that do not produce values.
6. Use getters for operations that conceptually access properties.
7. Use setters for operations that conceptually change properties.
8. Use inclusive start and exclusive end parameters to accept a range.
### Style and Structure
1. Prefer `final` over `var` when variable values won't change.
2. Use `const` for compile-time constants.
3. Keep files focused on a single responsibility.
4. Limit file length to maintain readability.
5. Group related functionality together.
6. Prefer making declarations private.
### Imports & Files
1. Don't import libraries inside the `src` directory of another package.
2. Prefer relative import paths within a package.
3. Don't use `/lib/` or `../` in import paths.
4. Consider writing a library-level doc comment for library files.
### 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` when disambiguating a `FutureOr` whose type argument could be `Object`.
6. Initialize fields at their declaration when possible.
7. Use initializing formals when possible.
8. Use `;` instead of `{}` for empty constructor bodies.
9. Use `rethrow` to rethrow a caught exception.
10. Override `hashCode` if you override `==`.
11. Make your `==` operator obey the mathematical rules of equality.
### Documentation
1. Use `///` doc comments to document members and types; don't use block comments for documentation.
2. Prefer writing doc comments for public APIs.
3. Start doc comments with a single-sentence summary.
4. Use square brackets in doc comments to refer to in-scope identifiers.
### Flutter Best Practices
1. Extract reusable widgets into separate components.
2. Use `StatelessWidget` when possible.
3. Keep build methods simple and focused.
4. Avoid unnecessary `StatefulWidget`s.
5. Keep state as local as possible.
6. Use `const` constructors when possible.
7. Avoid expensive operations in build methods.
8. Implement pagination for large lists.
### Dart 3: 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. Record expressions use parentheses with comma-delimited positional and/or named fields, e.g. `('first', a: 2, b: true, 'last')`.
4. Record fields are accessed via built-in getters: positional fields as `$1`, `$2`, etc., and named fields by their name (e.g., `.a`).
5. Records are immutable: fields do not have setters.
6. Use records for functions that return multiple values; destructure with pattern matching: `var (name, age) = userInfo(json);`
7. Use type aliases (`typedef`) for record types to improve readability and maintainability.
8. Records are best for simple, immutable data aggregation; use classes for abstraction, encapsulation, and behavior.
### Dart 3: Patterns
1. Patterns 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. Use wildcard patterns (`_`) to ignore parts of a matched value.
5. Use rest elements (`...`, `...rest`) in list patterns to match arbitrary-length lists.
6. Use logical-or patterns (e.g., `case a || b`) to match multiple alternatives in a single case.
7. Add guard clauses (`when`) to further constrain when a case matches.
8. Use the `sealed` modifier on a class to enable exhaustiveness checking when switching over its subtypes.
### 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.
3. If you get "An InputDecorator...cannot have an unbounded width", constrain the width of widgets like `TextField`.
4. If you get a "setState called during build" error, do not call `setState` or `showDialog` directly inside the build method.
5. If you get "The ScrollController is attached to multiple scroll views", make sure each `ScrollController` is only attached to a single scrollable widget.
6. If you get a "RenderBox was not laid out" error, check for missing or unbounded constraints in your widget tree.
7. Use the Flutter Inspector and review widget constraints to debug layout issues.
### Testing
1. Write unit tests for business logic.
2. Write widget tests for UI components.
## /combined/flutter_dart_bloc_mocktail.md
# 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` 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.
### 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` when disambiguating a `FutureOr` 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.
### 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. Use square brackets in doc comments to refer to in-scope identifiers.
9. Use prose to explain parameters, return values, and exceptions.
10. Put doc comments before metadata annotations.
11. 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.
# 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;`
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.
# 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.
# 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.
18. When exposing public methods on a cubit, only use them to trigger state changes and return `void` or `Future`.
19. For blocs, avoid exposing custom public methods; trigger state changes by adding events.
20. 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()` to access a bloc or repository without listening for changes (e.g., in callbacks).
11. Use `context.watch()` inside the build method to listen for changes and trigger rebuilds.
12. Use `context.select()` 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).
### 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.
### 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`) 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.
## /combined/flutter_dart_bloc_mocktail__under_6K.md
# Effective Dart Rules
### Key Rules
1. Use class modifiers to control if your class can be extended or used as an interface.
2. Make your `==` operator obey the mathematical rules of equality and override `hashCode` if you override `==`.
3. Type annotate fields, variables, and parameters when the type isn't obvious.
4. Use `Future` as the return type of asynchronous members that do not produce values.
5. Use getters for operations that conceptually access properties and setters for operations that conceptually change properties.
6. Use collection literals when possible.
7. Use `whereType()` to filter a collection by type.
8. Initialize fields at their declaration when possible.
9. Use initializing formals when possible.
10. Use `rethrow` to rethrow a caught exception.
### Style & Structure
1. Prefer `final` over `var` when variable values won't change.
2. Use `const` for compile-time constants.
3. Keep files focused on a single responsibility.
4. Prefer making declarations private.
5. Prefer making fields and top-level variables `final`.
6. Consider making your constructor `const` if the class supports it.
# Dart 3 Updates
### Key Features
1. Use records to group multiple values into a single, immutable object.
2. Access record fields by position (`$1`, `$2`, ...) or by name.
3. Use record types as return types or parameters for functions that return multiple values.
4. Use patterns to destructure records, lists, and objects directly in variable declarations, switch statements, and if-case.
5. Use switch expressions and pattern matching for concise and exhaustive control flow.
6. Use sealed classes to restrict which classes can implement or extend a base class.
7. Use class modifiers: `base`, `interface`, `final`, and `sealed`.
# Flutter Best Practices
### Widgets & UI
1. Extract reusable widgets into separate components.
2. Use `StatelessWidget` when possible and avoid unnecessary `StatefulWidgets`.
3. Keep build methods simple and focused.
4. Keep state as local as possible.
5. Use `const` constructors when possible.
6. Avoid expensive operations in build methods.
7. Implement pagination for large lists.
# Common Flutter Errors
1. If you get a "RenderFlex overflowed" error, check if a `Row` or `Column` contains unconstrained widgets.
2. If you get "Vertical viewport was given unbounded height", ensure `ListView` or similar scrollable widgets inside a `Column` have a bounded height.
3. If you get "An InputDecorator...cannot have an unbounded width", constrain the width of widgets like `TextField`.
4. If you get a "setState called during build" error, do not call `setState` or `showDialog` directly inside the build method.
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.
# Bloc Rules
### Naming Conventions
1. Events should be named in the past tense, reflecting actions that have already occurred.
2. States should be nouns.
### State Modeling
1. Prefer sealed classes or subclasses for exclusive states to enforce exhaustiveness and type safety.
2. State classes should extend `Equatable`, use `@immutable`, implement a `copyWith` method, and use `const` constructors where possible.
### Bloc Architecture
1. Implement a custom `BlocObserver` to monitor all state changes and errors across blocs.
2. Keep business logic inside blocs/cubits, not in UI widgets.
3. Only emit new states when the data actually changes to avoid unnecessary rebuilds.
4. Separate your features into three layers: Presentation, Business Logic, and Data.
### Flutter Bloc Integration
1. Use `BlocProvider` to provide blocs to widget subtrees.
2. Use `BlocBuilder` to rebuild widgets in response to state changes.
3. Use `BlocListener` for side effects in response to state changes.
4. Use `context.select` for fine-grained rebuilds.
### Bloc Do's and Don'ts
1. Do not mutate state directly; always emit a new state.
2. Do not perform side effects inside blocs; use the UI layer or listen to state changes for side effects.
3. Do not use `context.read` in the build method to access state; use `BlocBuilder` or `context.watch` instead.
# Mocktail Rules
1. Use a `Fake` when you need a lightweight, custom implementation of a class for testing.
2. Use a `Mock` when you need to verify interactions or stub method responses.
3. Use `registerFallbackValue` to register a default value for a type used as an argument in a mock method.
4. Use `when(() => mock.method()).thenReturn(value)` to stub method calls, and `thenThrow(error)` to stub errors.
5. Use `verify(() => mock.method())` to check if a method was called; use `verifyNever(() => mock.method())` to check it was never called.
6. Always stub async methods (returning `Future` or `Future`) with `thenAnswer((_) async {})` or `thenReturn(Future.value(...))`.
## /combined/flutter_dart_change_notifier.md
# 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` 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.
### 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` when disambiguating a `FutureOr` 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.
### 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. Use square brackets in doc comments to refer to in-scope identifiers.
9. Use prose to explain parameters, return values, and exceptions.
10. Put doc comments before metadata annotations.
11. 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.
# 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;`
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.
# 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.
### 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- _items = [];
UnmodifiableListView
- 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` widget to rebuild only when relevant data changes.
```dart
return Consumer(
builder: (context, cart, child) => Stack(
children: [
if (child != null) child,
Text('Total price: \${cart.totalPrice}'),
],
),
child: const SomeExpensiveWidget(),
);
```
8. Always specify the generic type `` for `Consumer` and `Provider.of` 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(
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(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(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.
## /combined/flutter_dart_change_notifier__under_6K.md
# Flutter with ChangeNotifier Rules
## Core Dart Principles
1. Use class modifiers to control if your class can be extended or used as an interface.
2. Make your `==` operator obey the mathematical rules of equality and override `hashCode` if you override `==`.
3. Type annotate fields, variables, and parameters when the type isn't obvious.
4. Use `Future` as the return type of asynchronous members that do not produce values.
5. Use getters for operations that conceptually access properties and setters for operations that conceptually change properties.
6. Use collection literals when possible.
7. Use `whereType()` to filter a collection by type.
8. Initialize fields at their declaration when possible.
9. Use initializing formals when possible.
10. Use `rethrow` to rethrow a caught exception.
## Flutter Best Practices
1. Extract reusable widgets into separate components.
2. Use `StatelessWidget` when possible.
3. Keep build methods simple and focused.
4. Avoid unnecessary `StatefulWidget`s.
5. Keep state as local as possible.
6. Use `const` constructors when possible.
7. Avoid expensive operations in build methods.
8. Implement pagination for large lists.
## ChangeNotifier State Management
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
- _items = [];
UnmodifiableListView
- 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` widget to rebuild only when relevant data changes.
```dart
return Consumer(
builder: (context, cart, child) => Stack(
children: [
if (child != null) child,
Text('Total price: \${cart.totalPrice}'),
],
),
child: const SomeExpensiveWidget(),
);
```
8. Always specify the generic type `` for `Consumer` and `Provider.of` 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(
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(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(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.
## Dart 3 Modern Features
1. Use records for grouping values: `var user = ('John', age: 30)`.
2. Access record fields by position (`$1`) or name (`.age`).
3. Use patterns to destructure records, lists, and objects: `var (name, age) = user;`
4. Use switch expressions and pattern matching for concise, exhaustive control flow.
5. Use sealed classes for exhaustive `switch` and type safety.
## 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.
## /combined/flutter_dart_provider.md
# 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` 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.
### 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` when disambiguating a `FutureOr` 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.
### 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. Use square brackets in doc comments to refer to in-scope identifiers.
9. Use prose to explain parameters, return values, and exceptions.
10. Put doc comments before metadata annotations.
11. 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.
# 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;`
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.
# 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.
### 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();
```
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(create: (_) => Something()),
Provider(create: (_) => SomethingElse()),
],
child: someWidget,
)
```
6. Use `context.watch()` to listen to changes and rebuild the widget when `T` changes.
7. Use `context.read()` to access a provider without listening for changes (e.g., in callbacks).
8. Use `context.select(R selector(T value))` to listen to only a small part of `T` and optimize rebuilds.
```dart
final selected = context.select((model) => model.count);
```
9. Use `Consumer` or `Selector` widgets for fine-grained rebuilds when you cannot access a descendant `BuildContext`.
```dart
Consumer(
builder: (context, value, child) => Text('$value'),
)
```
10. To migrate from `ValueListenableProvider`, use `Provider` with `ValueListenableBuilder`.
```dart
ValueListenableBuilder(
valueListenable: myValueListenable,
builder: (context, value, _) {
return Provider.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.
## /combined/flutter_dart_provider__under_6K.md
# Flutter with Provider Rules
## Core Dart Principles
1. Use class modifiers to control if your class can be extended or used as an interface.
2. Make your `==` operator obey the mathematical rules of equality and override `hashCode` if you override `==`.
3. Type annotate fields, variables, and parameters when the type isn't obvious.
4. Use `Future` as the return type of asynchronous members that do not produce values.
5. Use getters for operations that conceptually access properties and setters for operations that conceptually change properties.
6. Use collection literals when possible.
7. Use `whereType()` to filter a collection by type.
8. Initialize fields at their declaration when possible.
9. Use initializing formals when possible.
10. Use `rethrow` to rethrow a caught exception.
## Flutter Best Practices
1. Extract reusable widgets into separate components.
2. Use `StatelessWidget` when possible.
3. Keep build methods simple and focused.
4. Avoid unnecessary `StatefulWidget`s.
5. Keep state as local as possible.
6. Use `const` constructors when possible.
7. Avoid expensive operations in build methods.
8. Implement pagination for large lists.
## Provider State Management
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();
```
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(create: (_) => Something()),
Provider(create: (_) => SomethingElse()),
],
child: someWidget,
)
```
6. Use `context.watch()` to listen to changes and rebuild the widget when `T` changes.
7. Use `context.read()` to access a provider without listening for changes (e.g., in callbacks).
8. Use `context.select(R selector(T value))` to listen to only a small part of `T` and optimize rebuilds.
```dart
final selected = context.select((model) => model.count);
```
9. Use `Consumer` or `Selector` widgets for fine-grained rebuilds when you cannot access a descendant `BuildContext`.
```dart
Consumer(
builder: (context, value, child) => Text('$value'),
)
```
10. To migrate from `ValueListenableProvider`, use `Provider` with `ValueListenableBuilder`.
```dart
ValueListenableBuilder(
valueListenable: myValueListenable,
builder: (context, value, _) {
return Provider.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.
## Dart 3 Modern Features
1. Use records for grouping values: `var user = ('John', age: 30)`.
2. Access record fields by position (`$1`) or name (`.age`).
3. Use patterns to destructure records, lists, and objects: `var (name, age) = user;`
4. Use switch expressions and pattern matching for concise, exhaustive control flow.
5. Use sealed classes for exhaustive `switch` and type safety.
## 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.
## /combined/flutter_dart_riverpod_mockito.md
# 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` 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.
### 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` when disambiguating a `FutureOr` 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.
### 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. Use square brackets in doc comments to refer to in-scope identifiers.
9. Use prose to explain parameters, return values, and exceptions.
10. Put doc comments before metadata annotations.
11. 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.
# 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;`
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.
# 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.
# Riverpod Rules
### 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 "Cannot use `ref` 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`: called when a provider is added to the tree.
- `didUpdateProvider`: called when a provider is updated.
- `didDisposeProvider`: called when a provider is disposed.
- `providerDidFail`: 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.
### 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()])` 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.
## /combined/flutter_dart_riverpod_mockito__under_6K.md
# Flutter with Riverpod and Mockito Rules
## Core Dart Principles
1. Use class modifiers (`base`, `final`, `sealed`, `interface`) to control class extension.
2. Override `hashCode` if you override `==`.
3. Type annotate fields, variables, and parameters when the type isn't obvious.
4. Use `Future` for asynchronous members without return values.
5. Use getters for property access and setters for property changes.
## Riverpod Fundamentals
1. Always wrap your app with `ProviderScope` at the root (directly in `runApp`).
2. Define provider variables as `final` and at the top level (global scope).
3. Use `Provider` for synchronous values that don't change.
4. Use `StateProvider` for simple state that can be modified from the UI.
5. Use `FutureProvider` for asynchronous operations like API calls.
6. Use `StreamProvider` for real-time data streams.
7. Use `NotifierProvider` for complex state logic with methods.
8. Use `AsyncNotifierProvider` for complex async state logic with methods.
9. Providers are lazy—network requests or logic inside a provider are only executed when the provider is first read.
## Ref Object Usage
1. Use `ref.watch` to reactively depend on other providers; the provider will rebuild when dependencies change.
2. When using `ref.watch` with asynchronous providers, use `.future` to await the value if you need the resolved result.
3. Use `ref.listen` to perform side effects when a provider changes.
4. Use `ref.read` only when you cannot use `ref.watch`, such as inside Notifier methods or event handlers.
5. Be cautious with `ref.read`, as providers not being listened to may destroy their state if not actively watched.
## State Management with Notifiers
1. Use Notifiers to expose methods for performing side effects and modifying provider state.
2. Expose public methods on Notifiers for UI to trigger state changes or side effects.
3. In UI event handlers (e.g., button `onPressed`), use `ref.read` to call Notifier methods.
4. 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.
5. Always handle loading and error states in the UI when performing side effects.
6. Do not perform side effects directly inside provider constructors or build methods.
## Auto Dispose & State Disposal
1. By default, with code generation, provider state is destroyed when the provider stops being listened to.
2. Opt out of automatic disposal by setting `keepAlive: true` (codegen) or using `ref.keepAlive()` (manual).
3. When not using code generation, enable `.autoDispose` on providers to activate automatic disposal.
4. Always enable automatic disposal for providers that receive parameters to prevent memory leaks.
5. Use `ref.onDispose` to register cleanup logic that runs when provider state is destroyed.
## Passing Arguments to Providers
1. Use provider "families" to pass arguments to providers; add `.family` after the provider 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. The equality (`==`) of provider parameters determines caching—ensure parameters have consistent equality.
## Testing with Riverpod
1. Always create a new `ProviderContainer` (unit tests) or `ProviderScope` (widget tests) for each test.
2. In unit tests, never share `ProviderContainer` instances between tests:
```dart
final container = createContainer();
expect(container.read(provider), equals('some value'));
```
3. In widget tests, always wrap your widget tree with `ProviderScope`:
```dart
await tester.pumpWidget(
const ProviderScope(child: YourWidgetYouWantToTest()),
);
```
4. Obtain a `ProviderContainer` in widget tests using `ProviderScope.containerOf(BuildContext)`:
```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:
```dart
expect(container.read(provider), 'some value');
```
6. Use the `overrides` parameter to inject mocks or fakes for providers in your tests.
7. Use `container.listen` to spy on changes in a provider for assertions.
## Mockito Best Practices
1. Use a `Fake` when you want a lightweight, custom implementation of a class for testing.
2. Use a `Mock` when you need to verify interactions (method calls, arguments, call counts).
3. Use `@GenerateMocks([YourClass])` or `@GenerateNiceMocks([MockSpec()])` to generate mock classes.
4. Run `flutter pub run build_runner build` after adding mock annotations to generate the mock files.
5. Create mock instances from generated classes (e.g., `var mock = MockCat();`).
6. Use `when(mock.method()).thenReturn(value)` to stub method calls, and `when(mock.method()).thenThrow(error)` to stub errors.
7. Use `thenAnswer` to calculate a response at call time: `when(mock.method()).thenAnswer((_) => value);`.
## Dart 3 Modern Features
1. Use records for grouping values: `var user = ('John', age: 30, isAdmin: true);`.
2. Access record fields by position (`$1`, `$2`, ...) or by name: `user.$1`, `user.age`.
3. Use patterns to destructure records, lists, and objects: `var (name, :age) = user;`.
## Riverpod and Mockito Integration
1. When testing Riverpod providers, use Mockito to mock dependencies.
## /combined/flutter_with_bloc.md
# Flutter with Bloc - Combined Rules
# 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` 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.
### 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` when disambiguating a `FutureOr` 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.
### 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. Use square brackets in doc comments to refer to in-scope identifiers.
9. Use prose to explain parameters, return values, and exceptions.
10. Put doc comments before metadata annotations.
11. 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.
# 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;`
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.
# 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.
# 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.
18. When exposing public methods on a cubit, only use them to trigger state changes and return `void` or `Future`.
19. For blocs, avoid exposing custom public methods; trigger state changes by adding events.
20. 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()` to access a bloc or repository without listening for changes (e.g., in callbacks).
11. Use `context.watch()` inside the build method to listen for changes and trigger rebuilds.
12. Use `context.select()` 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).
### 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.
## /combined/flutter_with_bloc__under_6K.md
# Flutter with Bloc Rules
## Core Dart Principles
1. Use class modifiers (`base`, `final`, `sealed`, `interface`) to control class extension.
2. Override `hashCode` if you override `==`.
3. Type annotate fields, variables, and parameters when the type isn't obvious.
4. Use `Future` for asynchronous members without return values.
5. Use getters for property access and setters for property changes.
6. Use collection literals when possible.
7. Use `whereType()` to filter collections by type.
8. Initialize fields at declaration when possible.
9. Use initializing formals when possible.
10. Use `rethrow` to rethrow caught exceptions.
## Flutter Best Practices
1. Separate app into UI Layer (presentation) and Data Layer (business logic).
2. Keep views focused on presentation and extract reusable widgets.
3. Use StatelessWidget when possible and avoid unnecessary StatefulWidgets.
4. Keep build methods simple and focused.
5. Keep state as local as possible.
6. Use const constructors when possible.
7. Avoid expensive operations in build methods.
8. Implement pagination for large lists.
9. Keep files focused on a single responsibility.
10. Prefer making declarations private.
## Bloc Naming Conventions
1. Events should be named in the past tense, reflecting actions that have already occurred. Use the format: `BlocSubject` + optional noun + verb (event). For initial load events, use `BlocSubjectStarted`.
2. States should be nouns. For subclasses, use `BlocSubject` + `Initial` | `Success` | `Failure` | `InProgress`. For a single class, use `BlocSubjectState` with a `Status` enum (`initial`, `success`, `failure`, `loading`).
3. Prefer sealed classes or subclasses for exclusive states.
4. State classes should extend `Equatable`, use `@immutable`, implement a `copyWith` method, and use `const` constructors where possible. Always pass all relevant properties to the `props` getter when using Equatable to ensure proper equality comparisons.
5. Use a single concrete class with a status enum for non-exclusive states or when properties are frequently shared, but be aware this can lead to bloated and less type-safe code.
## Bloc Architecture
1. Implement a custom `BlocObserver` to monitor all state changes and errors across blocs for logging, analytics, or debugging. Override `onChange` and `onError` for global or per-bloc error handling.
2. Keep business logic inside blocs/cubits, not in UI widgets.
3. Only emit new states when the data actually changes to avoid unnecessary rebuilds.
4. Override `onError` in both `Cubit` and `BlocObserver` for robust error management.
5. Use `BlocProvider` to provide blocs to widget subtrees.
6. Use `BlocBuilder` to rebuild widgets in response to state changes.
7. Use `context.select` for fine-grained rebuilds, but avoid using it at the root of a widget tree, as it will cause the entire widget to rebuild on every state change.
8. Use `context.read()` for accessing a bloc instance without listening for changes (e.g., in callbacks), but avoid using it in `build` methods for reading state, as it won't trigger rebuilds.
9. Do not mutate state directly; always emit a new state.
10. Do not perform side effects inside blocs; use the UI layer or listen to state changes for side effects.
11. Separate your features into three layers: Presentation (UI), Business Logic (blocs/cubits), and Data (repositories/providers).
## Bloc Implementation
1. Use `Cubit` for simple state management without events; use `Bloc` for complex, event-driven state management.
2. Define the initial state by passing it to the superclass.
3. Only use the `emit` method inside a `Cubit` or `Bloc`.
4. UI components should listen to state changes and update only in response to new states.
5. Duplicate states are ignored; no state change will occur.
6. Use event transformers in `Bloc` for advanced event processing.
7. Prefer `Cubit` for simplicity and less boilerplate; prefer `Bloc` for traceability and advanced event handling.
## Flutter Bloc Integration
1. Use `BlocBuilder` to rebuild widgets in response to bloc or cubit state changes.
2. Use `BlocListener` to perform side effects in response to state changes.
3. Use `BlocConsumer` when you need both `BlocBuilder` and `BlocListener` functionality.
4. Use `BlocProvider` to provide blocs to widget subtrees via dependency injection.
5. Use `MultiBlocProvider` to provide multiple blocs and avoid deeply nested providers.
## Dart 3 Modern Features
1. Use records for grouping values: `var user = ('John', age: 30, isAdmin: true);`. Records are anonymous, immutable, aggregate types that bundle multiple objects into a single value.
2. Access record fields by position (`$1`, `$2`, ...) or by name: `user.$1`, `user.age`. Records automatically define `hashCode` and `==` based on structure and field values.
3. Use patterns to destructure records, lists, and objects. Example: `var (name, :age) = user;` or `if (response case {'user': {'name': String name}}) { print('User $name'); }`.
4. Use switch expressions and pattern matching for concise, exhaustive control flow.
5. Use sealed classes for exhaustive `switch` and type safety.
## /combined/flutter_with_riverpod.md
# Flutter with Riverpod - Combined 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((ref) => 0);
final provider = Provider((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((ref) {
final controller = StreamController();
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');
}
}
```
### 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` 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.
### 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` when disambiguating a `FutureOr` 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.
### 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. Use square brackets in doc comments to refer to in-scope identifiers.
9. Use prose to explain parameters, return values, and exceptions.
10. Put doc comments before metadata annotations.
11. 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.
### 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;`
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.
### 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.
### 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 "Cannot use `ref` 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`: called when a provider is added to the tree.
- `didUpdateProvider`: called when a provider is updated.
- `didDisposeProvider`: called when a provider is disposed.
- `providerDidFail`: 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.
## /combined/flutter_with_riverpod__under_6K.md
# Flutter with Riverpod Rules
## Core Dart Principles
1. Use class modifiers (`base`, `final`, `sealed`, `interface`) to control class extension.
2. Override `hashCode` if you override `==`.
3. Type annotate fields, variables, and parameters when the type isn't obvious.
4. Use `Future` for asynchronous members without return values.
5. Use getters for property access and setters for property changes.
6. Use collection literals when possible.
7. Use `whereType()` to filter collections by type.
8. Initialize fields at declaration when possible.
9. Use initializing formals when possible.
10. Use `rethrow` to rethrow caught exceptions.
## Riverpod Fundamentals
1. Always wrap your app with `ProviderScope` at the root (directly in `runApp`).
2. Define provider variables as `final` and at the top level (global scope).
3. Use `Consumer` or `ConsumerWidget` in your UI to access providers via a `ref` object.
4. Use `Provider` for synchronous values that don't change.
5. Use `StateProvider` for simple state that can be modified from the UI.
6. Use `FutureProvider` for asynchronous operations like API calls.
7. Use `StreamProvider` for real-time data streams.
8. Use `NotifierProvider` for complex state logic with methods.
9. Use `AsyncNotifierProvider` for complex async state logic with methods.
10. Always install and use `riverpod_lint` to enable IDE refactoring and enforce best practices.
11. Providers are lazy—network requests or logic inside a provider are only executed when the provider is first read.
12. Multiple widgets can listen to the same provider; the provider will only execute once and cache the result.
13. Obtain the `Ref` object as a parameter in provider functions (or `WidgetRef` in widgets) to access other providers and manage lifecycles.
**Example: Using @riverpod annotation**
```dart
@riverpod
int example(ref) {
return 0;
}
```
**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');
}
}
```
## Ref Object Usage
1. Use `ref.watch` to reactively depend on other providers; the provider will rebuild when dependencies change.
2. When using `ref.watch` with asynchronous providers, use `.future` to await the value if you need the resolved result.
3. Use `ref.listen` to perform side effects when a provider changes.
4. Use `ref.read` only when you cannot use `ref.watch`, such as inside Notifier methods or event handlers.
5. Be cautious with `ref.read`, as providers not being listened to may destroy their state if not actively watched.
## State Management with Notifiers
1. Use Notifiers to expose methods for performing side effects and modifying provider state.
2. Expose public methods on Notifiers for UI to trigger state changes or side effects.
3. In UI event handlers (e.g., button `onPressed`), use `ref.read` to call Notifier methods.
4. 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.
5. Always handle loading and error states in the UI when performing side effects.
## Auto Dispose & State Disposal
1. By default, with code generation, provider state is destroyed when the provider stops being listened to.
2. Opt out of automatic disposal by setting `keepAlive: true` (codegen) or using `ref.keepAlive()` (manual).
3. When not using code generation, enable `.autoDispose` on providers to activate automatic disposal.
4. Always enable automatic disposal for providers that receive parameters to prevent memory leaks.
5. Use `ref.onDispose` to register cleanup logic that runs when provider state is destroyed.
## Passing Arguments to Providers
1. Use provider "families" to pass arguments to providers; add `.family` after the provider 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. The equality (`==`) of provider parameters determines caching—ensure parameters have consistent equality.
5. Avoid passing objects that do not override `==` (such as plain `List` or `Map`) as provider parameters.
## Testing Providers
1. Always create a new `ProviderContainer` (unit tests) or `ProviderScope` (widget tests) for each test.
2. In unit tests, never share `ProviderContainer` instances between tests:
```dart
final container = createContainer();
expect(container.read(provider), equals('some value'));
```
3. In widget tests, always wrap your widget tree with `ProviderScope`:
```dart
await tester.pumpWidget(
const ProviderScope(child: YourWidgetYouWantToTest()),
);
```
4. Obtain a `ProviderContainer` in widget tests using `ProviderScope.containerOf(BuildContext)`:
```dart
final element = tester.element(find.byType(YourWidgetYouWantToTest));
final container = ProviderScope.containerOf(element);
expect(container.read(provider), 'some value');
```
5. Prefer mocking dependencies (such as repositories) used by Notifiers rather than mocking Notifiers directly.
## Dart 3 Modern Features
1. Use records for grouping values: `var user = ('John', age: 30, isAdmin: true);`.
2. Access record fields by position (`$1`, `$2`, ...) or by name: `user.$1`, `user.age`.
3. Use patterns to destructure records, lists, and objects: `var (name, :age) = user;`.
4. Use switch expressions and pattern matching for concise, exhaustive control flow.
5. Use sealed classes for exhaustive `switch` and type safety.
## /media/flutter_ai_rules.png
Binary file available at https://raw.githubusercontent.com/evanca/flutter-ai-rules/refs/heads/main/media/flutter_ai_rules.png
## /media/mocktail_md_01.png
Binary file available at https://raw.githubusercontent.com/evanca/flutter-ai-rules/refs/heads/main/media/mocktail_md_01.png
## /media/mocktail_md_02.png
Binary file available at https://raw.githubusercontent.com/evanca/flutter-ai-rules/refs/heads/main/media/mocktail_md_02.png
## /rules/bloc.md
# 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.
18. When exposing public methods on a cubit, only use them to trigger state changes and return `void` or `Future`.
19. For blocs, avoid exposing custom public methods; trigger state changes by adding events.
20. 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()` to access a bloc or repository without listening for changes (e.g., in callbacks).
11. Use `context.watch()` inside the build method to listen for changes and trigger rebuilds.
12. Use `context.select()` 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).
### 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.
TOTAL CHAR COUNT: 7810
## /rules/dart_3_updates.md
# 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.
TOTAL CHAR COUNT: 9612
## /rules/effective_dart.md
# 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` 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.
### 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` when disambiguating a `FutureOr` 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.
### 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. Use square brackets in doc comments to refer to in-scope identifiers.
9. Use prose to explain parameters, return values, and exceptions.
10. Put doc comments before metadata annotations.
11. 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.
TOTAL CHAR COUNT: 4993
## /rules/firebase/cloud_firestore.md
# Cloud Firestore Rules
### Database Selection
1. Choose Cloud Firestore for applications with rich data models requiring queryability, scalability, and high availability.
2. Use Cloud Firestore when your application needs complex, hierarchical data organization at scale using subcollections within documents.
3. Consider Cloud Firestore for applications where availability is of utmost importance (typical uptime performance of 99.999%).
4. Choose Cloud Firestore if you need to chain filters and combine filtering and sorting on a property in a single query.
5. Select Cloud Firestore when you need transactions that can atomically read and write data from any part of the database.
6. Consider Realtime Database instead for applications with simple data models requiring simple lookups and extremely low-latency synchronization (typical response times under 10ms).
### Setup and Configuration
1. Install Cloud Firestore using `flutter pub add cloud_firestore` in your Flutter project.
2. Import the package in your Dart code using `import 'package:cloud_firestore/cloud_firestore.dart';`.
3. Initialize Firestore with `final db = FirebaseFirestore.instance;` after Firebase has been initialized.
4. Select the database location closest to your users and compute resources to minimize latency.
5. Use multi-region locations for critical applications to maximize availability and durability.
6. Use regional locations for lower costs and lower write latency for latency-sensitive applications.
7. For iOS & macOS, consider using pre-compiled frameworks to improve build times by modifying your Podfile.
```
pod 'FirebaseFirestore',
:git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git',
:tag => 'IOS_SDK_VERSION'
```
### Document Structure
1. Avoid using document IDs `.` and `..` as they have special meaning in Firestore paths.
2. Avoid using forward slashes (`/`) in document IDs as they are used as path separators.
3. Do not use monotonically increasing document IDs (e.g., Customer1, Customer2, Customer3) as they can lead to hotspots that impact latency.
4. Use Firestore's automatic document IDs when possible to avoid hotspotting on writes.
```dart
// Create a new document with a generated ID
db.collection("users").add(user).then((DocumentReference doc) =>
print('DocumentSnapshot added with ID: ${doc.id}'));
```
5. Avoid using the following characters in field names as they require extra escaping: `.` (period), `[` (left bracket), `]` (right bracket), `*` (asterisk), `` ` `` (backtick).
6. Organize complex, hierarchical data using subcollections within documents rather than deeply nested objects.
### Indexing
1. Set collection-level index exemptions to reduce write latency and lower storage costs.
2. Disable Descending & Array indexing for fields that don't need them to improve performance.
3. Exempt string fields that hold long values and aren't used for querying from indexing to reduce storage costs.
4. Exempt fields with sequential values (like timestamps) from indexing if not used in queries to avoid the 500 writes per second limit.
5. Add single-field exemptions for TTL (time-to-live) fields to improve performance at higher traffic rates.
6. Exempt large array or map fields from indexing if not used in queries to avoid approaching the 40,000 index entries per document limit.
7. Be aware that queries in Firestore are indexed by default, with performance proportional to the size of your result set, not your dataset.
### Read and Write Operations
1. Use asynchronous calls instead of synchronous calls to minimize latency impact.
```dart
// Read data asynchronously
await db.collection("users").get().then((event) {
for (var doc in event.docs) {
print("${doc.id} => ${doc.data()}");
}
});
```
2. Do not use offsets for pagination; use cursors instead to avoid retrieving and billing for skipped documents.
3. Implement transaction retries when accessing Firestore directly through REST or RPC APIs.
4. Be aware of the limitations on the rate at which a single document can be updated.
5. For writing a large number of documents, consider using a bulk writer instead of the atomic batch writer.
6. When performing multiple independent operations (like a document lookup and a query), execute them in parallel rather than sequentially.
7. Remember that Firestore queries are shallow and only return documents in a particular collection or collection group, not subcollection data.
8. Be aware that Firestore has limits on write rates to individual documents (around 1 write per second per document) and indexes.
### Designing for Scale
1. Avoid high read or write rates to lexicographically close documents to prevent hotspotting.
2. Avoid creating new documents with monotonically increasing fields (like timestamps) at a very high rate.
3. Avoid deleting documents in a collection at a high rate.
4. Gradually increase traffic when writing to the database at a high rate.
5. Avoid queries that skip over recently deleted data by using the `start_at` method to find the best place to start.
6. For high-traffic applications, distribute writes across different document paths to avoid contention.
7. Be aware that Firestore scales automatically to around 1 million concurrent connections and 10,000 writes/second.
### Real-time Updates
1. Limit the number of simultaneous real-time listeners in your application.
2. Detach listeners when they are no longer needed to free up resources.
```dart
// Set up a listener
final subscription = db.collection("users")
.snapshots()
.listen((event) {
// Handle the data
});
// Later, when no longer needed:
subscription.cancel();
```
3. Use compound queries to filter data on the server side rather than filtering in the client.
4. Consider using server-side filtering to reduce the amount of data transferred to clients.
5. For large collections, use queries to limit the data being listened to rather than listening to the entire collection.
6. For presence functionality (knowing when a client is online/offline), implement custom presence solutions as described in Firebase documentation.
### Security
1. Always use Firebase Security Rules to protect your Firestore data.
2. Validate user input before submitting it to Firestore to prevent injection attacks.
3. Use transactions for operations that require atomic updates to multiple documents.
4. Implement proper error handling for Firestore operations to handle potential failures gracefully.
5. Never store sensitive information in Firestore without proper access controls.
6. Be aware that Firestore security rules don't cascade unless you use a wildcard.
7. Remember that if a query's results might contain data the user doesn't have access to, the entire query fails.
## /rules/firebase/cloud_functions.md
# Firebase Cloud Functions Rules
### Setup and Configuration
1. Install the Cloud Functions plugin using `flutter pub add cloud_functions`.
2. Import the plugin in your Dart code using `import 'package:cloud_functions/cloud_functions.dart';`.
3. Initialize Firebase before using any Cloud Functions features.
4. Access the Cloud Functions instance using `final functions = FirebaseFunctions.instance;`.
5. For region-specific deployments, specify the region when getting the instance.
6. Deploy your callable functions to Firebase before attempting to call them from your Flutter app.
7. Consider implementing App Check to prevent abuse of your Cloud Functions.
### Calling Functions
1. Call a Cloud Function using the `httpsCallable` method followed by the `call` method.
```dart
final result = await FirebaseFunctions.instance
.httpsCallable('functionName')
.call(data);
```
2. Pass data to functions as a Map, which will be automatically serialized to JSON.
```dart
final result = await FirebaseFunctions.instance
.httpsCallable('addMessage')
.call({
"text": messageText,
"push": true,
});
```
3. Access the function result data using the `data` property of the returned object.
```dart
final responseData = result.data;
```
4. Be aware that the data returned from a function is automatically deserialized from JSON.
5. Use type casting to convert the returned data to the expected type.
```dart
final responseString = result.data as String;
```
6. Avoid passing sensitive information like authentication tokens in function parameters, as they are automatically included by the SDK.
7. Keep function names consistent between your client code and server-side implementations.
### Error Handling
1. Use try-catch blocks to handle errors when calling Cloud Functions.
```dart
try {
final result = await FirebaseFunctions.instance
.httpsCallable('functionName')
.call(data);
// Handle successful result
} catch (e) {
if (e is FirebaseFunctionsException) {
// Handle specific function error
print('Error code: ${e.code}');
print('Error message: ${e.message}');
print('Error details: ${e.details}');
} else {
// Handle general error
print('Error: $e');
}
}
```
2. Check for `FirebaseFunctionsException` to handle specific function errors.
3. Access error details using the `code`, `message`, and `details` properties of `FirebaseFunctionsException`.
4. Implement proper error handling for network connectivity issues.
5. Handle timeouts appropriately, especially for long-running functions.
6. Provide meaningful error messages to users when function calls fail.
7. Consider implementing retry logic for transient errors.
### Performance Optimization
1. Set appropriate timeouts for function calls based on expected execution time.
```dart
final functions = FirebaseFunctions.instance;
functions.useFunctionsEmulator('localhost', 5001);
final callable = functions.httpsCallable(
'functionName',
options: HttpsCallableOptions(
timeout: const Duration(seconds: 30),
),
);
```
2. Minimize the amount of data passed to and from functions to reduce latency.
3. Use batch operations when possible to reduce the number of function calls.
4. Consider implementing client-side caching for frequently used function results.
5. Monitor function performance using Firebase Console to identify bottlenecks.
6. Be mindful of cold starts for infrequently used functions.
7. Implement proper loading states in your UI while waiting for function responses.
### Testing and Development
1. Use the Firebase Emulator Suite for local development and testing.
```dart
FirebaseFunctions.instance.useFunctionsEmulator('localhost', 5001);
```
2. Test functions with various input data to ensure robust error handling.
3. Implement unit tests for client-side function calling logic.
4. Use integration tests to verify end-to-end function behavior.
5. Test functions with both valid and invalid inputs to ensure proper validation.
6. Verify that functions handle authentication correctly.
7. Test functions with different user roles and permissions to ensure proper access control.
## /rules/firebase/firebase_analytics.md
# Firebase Analytics Rules
### Setup and Configuration
1. Install Firebase Analytics using `flutter pub add firebase_analytics` in your Flutter project.
2. Rebuild your Flutter application using `flutter run` after installation.
3. Import the package in your Dart code using `import 'package:firebase_analytics/firebase_analytics.dart';`.
4. Create a new Firebase Analytics instance by accessing the `instance` property on `FirebaseAnalytics`.
```dart
FirebaseAnalytics analytics = FirebaseAnalytics.instance;
```
5. Initialize Firebase before using any Firebase Analytics features.
### Event Logging
1. Use predefined event types when possible to get maximum detail in reports and benefit from the latest Google Analytics features.
2. Log events using the library's dedicated methods for recommended event types.
```dart
await FirebaseAnalytics.instance.logSelectContent(
contentType: "image",
itemId: itemId,
);
```
3. Alternatively, use the general `logEvent()` method for both predefined and custom events.
```dart
await FirebaseAnalytics.instance.logEvent(
name: "select_content",
parameters: {
"content_type": "image",
"item_id": itemId,
},
);
```
4. For custom events, use the `logEvent()` method with a descriptive name and relevant parameters.
```dart
await FirebaseAnalytics.instance.logEvent(
name: "share_image",
parameters: {
"image_name": name,
"full_text": text,
},
);
```
5. Event names are case-sensitive; logging two events whose names differ only in case will result in two distinct events.
6. You can log up to 500 different Analytics Event types in your app with no limit on the total volume of events.
### Parameters and Properties
1. Parameter names can be up to 40 characters long and must start with an alphabetic character.
2. Parameter names must contain only alphanumeric characters and underscores.
3. String parameter values can be up to 100 characters long.
4. The "firebase_", "google_" and "ga_" prefixes are reserved and shouldn't be used for parameter names.
5. Use custom parameters for non-numerical event parameter data (dimensions) or numerical data (metrics).
6. Register custom dimensions or metrics in the Analytics console to ensure they appear in reports.
7. Set default event parameters using `setDefaultEventParameters()` to associate parameters with all future events.
```dart
// Not supported on web
await FirebaseAnalytics.instance
.setDefaultEventParameters({
version: '1.2.3'
});
```
8. To clear a default parameter, call `setDefaultEventParameters()` with the parameter set to `null`.
### User Properties
1. Set user properties to describe segments of your user base using the `setUserProperty()` method.
```dart
await FirebaseAnalytics.instance
.setUserProperty(
name: 'favorite_food',
value: favoriteFood,
);
```
2. Create custom definitions for user properties in the Analytics console before using them in your app.
3. Use user properties for creating custom definitions, applying comparisons in reports, or as audience evaluation criteria.
4. Keep user property names and values concise and relevant to your analytics needs.
### Best Practices
1. Analytics automatically logs some events and user properties; you don't need to add code to enable them.
2. Request necessary permissions before collecting user data, especially on platforms with strict privacy controls.
3. Avoid logging sensitive or personally identifiable information in events or user properties.
4. Use consistent naming conventions for custom events and parameters to make analysis easier.
5. Group related events to track user flows and conversion funnels effectively.
6. Consider the analytics data you'll need for decision-making before implementing tracking to ensure you collect the right information.
7. Test your analytics implementation to verify events are being logged correctly before deploying to production.
## /rules/firebase/firebase_app_check.md
# Firebase App Check Rules
### Setup and Configuration
1. Install the Firebase App Check plugin using `flutter pub add firebase_app_check`.
2. Import the plugin in your Dart code using `import 'package:firebase_app_check/firebase_app_check.dart';`.
3. Register your apps in the Firebase console under Project Settings > App Check before using the service.
4. Consider setting a custom time-to-live (TTL) for App Check tokens based on your security and performance needs.
5. Be aware that shorter TTLs provide stronger security but may impact performance and consume quota faster.
6. Initialize App Check after Firebase initialization but before using any Firebase services.
```dart
await Firebase.initializeApp();
await FirebaseAppCheck.instance.activate(
webProvider: ReCaptchaV3Provider('recaptcha-v3-site-key'),
androidProvider: AndroidProvider.playIntegrity,
appleProvider: AppleProvider.deviceCheck,
);
```
7. For web applications, obtain a reCAPTCHA v3 site key from the Firebase console and use it with the `ReCaptchaV3Provider`.
### Provider Selection
1. For Android, choose the appropriate provider based on your requirements:
- `AndroidProvider.playIntegrity` (default): Uses Play Integrity API for app verification
- `AndroidProvider.safetyNet`: Uses the legacy SafetyNet API (not recommended for new apps)
- `AndroidProvider.debug`: For development and testing environments only
2. For Apple platforms (iOS/macOS), choose the appropriate provider:
- `AppleProvider.deviceCheck` (default): Works on iOS 11+ and macOS 10.15+
- `AppleProvider.appAttest`: Enhanced security on iOS 14+ and macOS 14+
- `AppleProvider.appAttestWithDeviceCheckFallback`: Uses App Attest with fallback to Device Check
- `AppleProvider.debug`: For development and testing environments only
3. For web platforms, use one of the following providers:
- `ReCaptchaV3Provider`: Standard reCAPTCHA v3 verification
- `ReCaptchaEnterpriseProvider`: Enhanced version with additional features
### Development and Testing
1. Use the debug provider during development to run your app in emulators or CI environments.
```dart
await FirebaseAppCheck.instance.activate(
androidProvider: AndroidProvider.debug,
appleProvider: AppleProvider.debug,
);
```
2. For iOS debug builds, enable debug logging by adding `-FIRDebugEnabled` to the Arguments Passed on Launch in Xcode.
3. For web debug builds, enable debug mode by setting `self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;` in your `web/index.html` file.
4. Register the debug tokens displayed in the console in the Firebase console's App Check section.
5. Never use debug providers or share debug tokens in production builds.
6. Keep debug tokens private and don't commit them to public repositories.
7. Revoke compromised debug tokens immediately from the Firebase console.
### Enforcement and Monitoring
1. Monitor App Check metrics before enabling enforcement to ensure it won't disrupt legitimate users.
2. Enable enforcement gradually, starting with non-critical Firebase services.
3. Monitor request metrics for Realtime Database, Cloud Firestore, Cloud Storage, and Authentication to understand usage patterns.
4. Be aware that once enforcement is enabled, only registered apps with valid App Check tokens can access your Firebase resources.
5. Consider enabling enforcement sooner if you observe suspicious usage of your app resources.
6. Use App Check in combination with Firebase Security Rules for comprehensive security.
7. Implement proper error handling for App Check verification failures in your app.
### Security Best Practices
1. Never disable App Check in production builds once you've enabled it.
2. Implement a fallback mechanism for handling App Check verification failures.
3. Regularly review App Check metrics to identify potential abuse patterns.
4. Use App Check in conjunction with other security measures like authentication and security rules.
5. Be aware that App Check tokens are automatically refreshed at approximately half the TTL duration.
6. For high-security applications, use the shortest practical TTL for App Check tokens.
7. Implement server-side verification for critical operations using Firebase Admin SDK.
The content has been capped at 50000 tokens, and files over NaN bytes have been omitted. The user could consider applying other filters to refine the result. The better and more specific the context, the better the LLM can follow instructions. If the context seems verbose, the user can refine the filter using uithub. Thank you for using https://uithub.com - Perfect LLM context for any GitHub repo.