mirror of
https://github.com/kuhyx/diet-guard.git
synced 2026-11-10 20:27:11 +01:00
Some checks failed
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Tests / test (3.10) (push) Has been cancelled
Tests / test (3.11) (push) Has been cancelled
Tests / test (3.12) (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
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. Includes the patch bump this repo's bump-patch-version hook applies to app/pubspec.yaml on every commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A47aX5DJwmA3QFn3pkEWi7
184 lines
6.0 KiB
Markdown
184 lines
6.0 KiB
Markdown
<!--
|
|
Vendored from flutter/flutter docs/rules/rules.md, split across five files
|
|
so each stays under this repo's 250-line cap. Upstream content: edit it here
|
|
only to re-sync with upstream, not to record decisions about this repo --
|
|
those belong in CLAUDE.md.
|
|
-->
|
|
|
|
# Architecture, lint rules, state and data
|
|
|
|
_Vendored Flutter/Dart AI rules. See [CLAUDE.md](../CLAUDE.md)._
|
|
|
|
### Application Architecture
|
|
* **Separation of Concerns:** Aim for separation of concerns similar to MVC/MVVM, with defined Model,
|
|
View, and ViewModel/Controller roles.
|
|
* **Logical Layers:** Organize the project into logical layers:
|
|
* Presentation (widgets, screens)
|
|
* Domain (business logic classes)
|
|
* Data (model classes, API clients)
|
|
* Core (shared classes, utilities, and extension types)
|
|
* **Feature-based Organization:** For larger projects, organize code by feature,
|
|
where each feature has its own presentation, domain, and data subfolders. This
|
|
improves navigability and scalability.
|
|
|
|
### Lint Rules
|
|
|
|
Include the package in the `analysis_options.yaml` file. Use the following
|
|
`analysis_options.yaml` file as a starting point:
|
|
|
|
```yaml
|
|
include: package:flutter_lints/flutter.yaml
|
|
|
|
linter:
|
|
rules:
|
|
# Add additional lint rules here:
|
|
# avoid_print: false
|
|
# prefer_single_quotes: true
|
|
```
|
|
|
|
#### 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 `Streams` and `StreamBuilder` for handling a sequence of
|
|
asynchronous events.
|
|
* **Futures:** Use `Futures` and `FutureBuilder` for handling a single
|
|
asynchronous operation that will complete in the future.
|
|
* **ValueNotifier:** Use `ValueNotifier` with `ValueListenableBuilder` for
|
|
simple, local state that involves a single value.
|
|
|
|
```dart
|
|
// 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 `ListenableBuilder` to listen to changes from a
|
|
`ChangeNotifier` or other `Listenable`.
|
|
* **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, `provider` can 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_router` package for declarative navigation, deep
|
|
linking, and web support.
|
|
* **GoRouter Setup:** To use `go_router`, first add it to your `pubspec.yaml`
|
|
using the `pub` tool's `add` command.
|
|
|
|
```dart
|
|
// 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`'s `redirect` property 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 `Navigator` for short-lived screens that do
|
|
not need to be deep-linkable, such as dialogs or temporary views.
|
|
|
|
```dart
|
|
// 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_serializable` and `json_annotation` for
|
|
parsing and encoding JSON data.
|
|
* **Field Renaming:** When encoding data, use `fieldRename: FieldRename.snake`
|
|
to convert Dart's camelCase fields to snake_case JSON keys.
|
|
|
|
```dart
|
|
// 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 `log` function from `dart:developer` for
|
|
structured logging that integrates with Dart DevTools.
|
|
|
|
```dart
|
|
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,
|
|
);
|
|
}
|
|
```
|