Renames this repo's markdown onto README.md / CLAUDE*.md / DOCS*.md / TODO*.md so a future session can tell a live task from a finished record without reading the file to find out. TODO files carry the literal "REMOVE ME AFTER FINISH" and are deleted when the work lands. Enforced by the md-naming pre-commit hook and CI workflow, both delegating to the shared gate in kuhyx/utils so this repo's idea of a valid name cannot drift from every other repo's. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A47aX5DJwmA3QFn3pkEWi7
5.1 KiB
Flutter AI rules — state, routing and data
State management, data flow, routing, JSON serialization and logging.
Verbatim from flutter/flutter docs/rules/rules.md, split across four files only to keep each under the repo's 250-line cap. Re-fetch periodically to stay current; do not hand-edit.
State Management
-
Built-in Solutions: Prefer Flutter's built-in state management solutions. Do not use a third-party package unless explicitly requested.
-
Streams: Use
StreamsandStreamBuilderfor handling a sequence of asynchronous events. -
Futures: Use
FuturesandFutureBuilderfor handling a single asynchronous operation that will complete in the future. -
ValueNotifier: Use
ValueNotifierwithValueListenableBuilderfor simple, local state that involves a single value.// Define a ValueNotifier to hold the state. final ValueNotifier<int> _counter = ValueNotifier<int>(0); // Use ValueListenableBuilder to listen and rebuild. ValueListenableBuilder<int>( valueListenable: _counter, builder: (context, value, child) { return Text('Count: $value'); }, ); -
ChangeNotifier: For state that is more complex or shared across multiple widgets, use
ChangeNotifier. -
ListenableBuilder: Use
ListenableBuilderto listen to changes from aChangeNotifieror otherListenable. -
MVVM: When a more robust solution is needed, structure the app using the Model-View-ViewModel (MVVM) pattern.
-
Dependency Injection: Use simple manual constructor dependency injection to make a class's dependencies explicit in its API, and to manage dependencies between different layers of the application.
-
Provider: If a dependency injection solution beyond manual constructor injection is explicitly requested,
providercan be used to make services, repositories, or complex state objects available to the UI layer without tight coupling (note: this document generally defaults against third-party packages for state management unless explicitly requested).
Data Flow
- Data Structures: Define data structures (classes) to represent the data used in the application.
- Data Abstraction: Abstract data sources (e.g., API calls, database operations) using Repositories/Services to promote testability.
Routing
-
GoRouter: Use the
go_routerpackage for declarative navigation, deep linking, and web support. -
GoRouter Setup: To use
go_router, first add it to yourpubspec.yamlusing thepubtool'saddcommand.// 1. Add the dependency // flutter pub add go_router // 2. Configure the router final GoRouter _router = GoRouter( routes: <RouteBase>[ GoRoute( path: '/', builder: (context, state) => const HomeScreen(), routes: <RouteBase>[ GoRoute( path: 'details/:id', // Route with a path parameter builder: (context, state) { final String id = state.pathParameters['id']!; return DetailScreen(id: id); }, ), ], ), ], ); // 3. Use it in your MaterialApp MaterialApp.router( routerConfig: _router, ); -
Authentication Redirects: Configure
go_router'sredirectproperty to handle authentication flows, ensuring users are redirected to the login screen when unauthorized, and back to their intended destination after successful login. -
Navigator: Use the built-in
Navigatorfor short-lived screens that do not need to be deep-linkable, such as dialogs or temporary views.// Push a new screen onto the stack Navigator.push( context, MaterialPageRoute(builder: (context) => const DetailsScreen()), ); // Pop the current screen to go back Navigator.pop(context);
Data Handling & Serialization
-
JSON Serialization: Use
json_serializableandjson_annotationfor parsing and encoding JSON data. -
Field Renaming: When encoding data, use
fieldRename: FieldRename.snaketo convert Dart's camelCase fields to snake_case JSON keys.// In your model file import 'package:json_annotation/json_annotation.dart'; part 'user.g.dart'; @JsonSerializable(fieldRename: FieldRename.snake) class User { final String firstName; final String lastName; User({required this.firstName, required this.lastName}); factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); Map<String, dynamic> toJson() => _$UserToJson(this); }
Logging
-
Structured Logging: Use the
logfunction fromdart:developerfor structured logging that integrates with Dart DevTools.import 'dart:developer' as developer; // For simple messages developer.log('User logged in successfully.'); // For structured error logging try { // ... code that might fail } catch (e, s) { developer.log( 'Failed to fetch data', name: 'myapp.network', level: 1000, // SEVERE error: e, stackTrace: s, ); }