reis-mobile 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +24 -0
- package/.claude-plugin/plugin.json +12 -0
- package/CHANGELOG.md +103 -0
- package/LICENSE +21 -0
- package/README.md +631 -0
- package/THIRD_PARTY_NOTICES.md +105 -0
- package/agents/flutter-architect.md +92 -0
- package/agents/flutter-performance-engineer.md +103 -0
- package/agents/flutter-test-engineer.md +100 -0
- package/agents/lead-mobile.md +107 -0
- package/agents/mobile-code-reviewer.md +66 -0
- package/agents/mobile-staff-engineer.md +85 -0
- package/agents/plugin-native-expert.md +97 -0
- package/bin/reis-mobile.mjs +256 -0
- package/commands/debate.md +135 -0
- package/commands/doctor.md +23 -0
- package/commands/reis-mobile.md +50 -0
- package/commands/review.md +53 -0
- package/core/config/language.mjs +83 -0
- package/core/context/context-engine.mjs +113 -0
- package/core/detection/project-probe.mjs +104 -0
- package/core/detection/stack-detector.mjs +204 -0
- package/core/diagnostics/doctor.mjs +181 -0
- package/core/install/claude-plugin.mjs +76 -0
- package/core/paths.mjs +4 -0
- package/core/registry/frontmatter.mjs +81 -0
- package/core/registry/registry.mjs +140 -0
- package/core/router/intent-detector.mjs +75 -0
- package/core/router/intents.mjs +91 -0
- package/core/router/router.mjs +83 -0
- package/core/security/redact.mjs +24 -0
- package/package.json +35 -0
- package/skills/architecture-feature-first/SKILL.md +116 -0
- package/skills/bloc/SKILL.md +354 -0
- package/skills/code-review/SKILL.md +89 -0
- package/skills/dart-3-updates/SKILL.md +223 -0
- package/skills/dart-add-unit-test/SKILL.md +126 -0
- package/skills/dart-collect-coverage/SKILL.md +145 -0
- package/skills/dart-fix-runtime-errors/SKILL.md +170 -0
- package/skills/dart-generate-test-mocks/SKILL.md +159 -0
- package/skills/dart-migrate-to-checks-package/SKILL.md +532 -0
- package/skills/dart-resolve-package-conflicts/SKILL.md +120 -0
- package/skills/dart-run-static-analysis/SKILL.md +108 -0
- package/skills/dart-use-pattern-matching/SKILL.md +240 -0
- package/skills/dart-use-pattern-matching/examples/json_patterns.dart +85 -0
- package/skills/detect-mobile-stack/SKILL.md +33 -0
- package/skills/effective-dart/SKILL.md +187 -0
- package/skills/firebase-ai/SKILL.md +93 -0
- package/skills/firebase-analytics/SKILL.md +129 -0
- package/skills/firebase-app-check/SKILL.md +124 -0
- package/skills/firebase-auth/SKILL.md +224 -0
- package/skills/firebase-cloud-firestore/SKILL.md +165 -0
- package/skills/firebase-cloud-functions/SKILL.md +148 -0
- package/skills/firebase-crashlytics/SKILL.md +182 -0
- package/skills/firebase-data-connect/SKILL.md +80 -0
- package/skills/firebase-database/SKILL.md +224 -0
- package/skills/firebase-in-app-messaging/SKILL.md +111 -0
- package/skills/firebase-messaging/SKILL.md +201 -0
- package/skills/firebase-remote-config/SKILL.md +131 -0
- package/skills/firebase-storage/SKILL.md +158 -0
- package/skills/flutter-add-integration-test/SKILL.md +167 -0
- package/skills/flutter-add-widget-preview/SKILL.md +149 -0
- package/skills/flutter-add-widget-test/SKILL.md +158 -0
- package/skills/flutter-adding-home-screen-widgets/SKILL.md +205 -0
- package/skills/flutter-animating-apps/SKILL.md +185 -0
- package/skills/flutter-app-architecture/SKILL.md +80 -0
- package/skills/flutter-apply-architecture-best-practices/SKILL.md +166 -0
- package/skills/flutter-build-responsive-layout/SKILL.md +143 -0
- package/skills/flutter-building-forms/SKILL.md +125 -0
- package/skills/flutter-building-layouts/SKILL.md +131 -0
- package/skills/flutter-building-plugins/SKILL.md +209 -0
- package/skills/flutter-caching-data/SKILL.md +171 -0
- package/skills/flutter-change-notifier/SKILL.md +138 -0
- package/skills/flutter-embedding-native-views/SKILL.md +200 -0
- package/skills/flutter-errors/SKILL.md +126 -0
- package/skills/flutter-fix-layout-issues/SKILL.md +134 -0
- package/skills/flutter-handling-concurrency/SKILL.md +182 -0
- package/skills/flutter-implement-json-serialization/SKILL.md +157 -0
- package/skills/flutter-improving-accessibility/SKILL.md +113 -0
- package/skills/flutter-interoperating-with-native-apis/SKILL.md +130 -0
- package/skills/flutter-login-usecase/SKILL.md +38 -0
- package/skills/flutter-managing-state/SKILL.md +206 -0
- package/skills/flutter-project-audit/SKILL.md +66 -0
- package/skills/flutter-reducing-app-size/SKILL.md +103 -0
- package/skills/flutter-secure-token-store/SKILL.md +35 -0
- package/skills/flutter-setting-up-on-macos/SKILL.md +78 -0
- package/skills/flutter-setup-declarative-routing/SKILL.md +259 -0
- package/skills/flutter-setup-localization/SKILL.md +214 -0
- package/skills/flutter-theming-apps/SKILL.md +163 -0
- package/skills/flutter-use-http-package/SKILL.md +178 -0
- package/skills/flutter-widget-review/SKILL.md +54 -0
- package/skills/flutter-working-with-databases/SKILL.md +203 -0
- package/skills/flutterfire-configure/SKILL.md +143 -0
- package/skills/mobile-security-audit/SKILL.md +101 -0
- package/skills/mockito/SKILL.md +149 -0
- package/skills/mocktail/SKILL.md +122 -0
- package/skills/patrol-e2e-testing/SKILL.md +224 -0
- package/skills/provider/SKILL.md +139 -0
- package/skills/riverpod/SKILL.md +246 -0
- package/skills/testing/SKILL.md +50 -0
- package/stacks/android/stack.json +8 -0
- package/stacks/flutter/stack.json +8 -0
- package/stacks/ios/stack.json +8 -0
- package/stacks/kotlin-multiplatform/stack.json +8 -0
- package/stacks/react-native/stack.json +8 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: architecture-feature-first
|
|
3
|
+
description: Structures Flutter apps using layered architecture (UI / Logic / Data) with feature-first file organization. Use when creating new features, designing the project structure, adding repositories/services/view models (or cubits/providers/notifiers), or wiring dependency injection. State management agnostic.
|
|
4
|
+
intents: [architecture]
|
|
5
|
+
stacks: [flutter]
|
|
6
|
+
source: https://github.com/evanca/flutter-ai-rules
|
|
7
|
+
license: MIT
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Flutter Architecture — Feature-First Skill
|
|
11
|
+
|
|
12
|
+
This skill defines how to design, structure, and implement Flutter applications using the recommended **layered architecture** with **feature-first** file organization.
|
|
13
|
+
|
|
14
|
+
It is **state management agnostic**: the business logic holder in the UI layer may be named ViewModel, Controller, Cubit, Bloc, Provider, or Notifier — depending on the chosen state management approach. The architectural rules apply equally to all of them.
|
|
15
|
+
|
|
16
|
+
## When to Use
|
|
17
|
+
|
|
18
|
+
Use this skill when:
|
|
19
|
+
|
|
20
|
+
* Designing the folder/file structure of a new Flutter app or feature.
|
|
21
|
+
* Creating a new View, ViewModel, Repository, or Service.
|
|
22
|
+
* Deciding which layer owns a piece of logic.
|
|
23
|
+
* Wiring dependency injection between components.
|
|
24
|
+
* Adding a domain (logic) layer for complex business logic.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 1. Layers
|
|
29
|
+
|
|
30
|
+
Separate every app into a **UI Layer** and a **Data Layer**. Add a **Logic (Domain) Layer** between them only for complex apps.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
34
|
+
│ UI Layer │ Views + business logic holders │
|
|
35
|
+
│ │ (ViewModel / Cubit / Controller / Provider) │
|
|
36
|
+
├──────────────────────────────────────────────────────────────┤
|
|
37
|
+
│ Logic Layer │ Use Cases / Interactors (optional) │
|
|
38
|
+
├──────────────────────────────────────────────────────────────┤
|
|
39
|
+
│ Data Layer │ Repositories + Services │
|
|
40
|
+
└──────────────────────────────────────────────────────────────┘
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Rules:**
|
|
44
|
+
- Only adjacent layers may communicate. The UI layer must never access a Service directly.
|
|
45
|
+
- The Logic layer is added **only** when business logic is too complex for the business logic holder or is reused across multiple screens.
|
|
46
|
+
- Data changes always happen in the Data layer (SSOT = Repository). No mutation in UI or Logic layers.
|
|
47
|
+
- Follow unidirectional data flow: state flows **down** (Data → UI), events flow **up** (UI → Data).
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 2. Feature-First File Structure
|
|
52
|
+
|
|
53
|
+
Organize code by **feature**, not by type. Group all layers belonging to one feature together in a single directory.
|
|
54
|
+
|
|
55
|
+
Each feature directory contains the files needed for that feature, named according to the chosen state management approach:
|
|
56
|
+
|
|
57
|
+
| Approach | Business logic holder file |
|
|
58
|
+
|---|---|
|
|
59
|
+
| MVVM / ChangeNotifier | `*_viewmodel.dart` / `*_controller.dart` |
|
|
60
|
+
| BLoC | `*_cubit.dart` / `*_bloc.dart` |
|
|
61
|
+
| Provider / Riverpod | `*_provider.dart` / `*_notifier.dart` |
|
|
62
|
+
|
|
63
|
+
Example: an `auth` feature would have files like `auth_viewmodel.dart` (or `auth_cubit.dart`, `auth_provider.dart` depending on approach), `login_screen.dart`, and optionally `login_usecase.dart`.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 3. Component Responsibilities
|
|
68
|
+
|
|
69
|
+
### View
|
|
70
|
+
- Describes how to present data to the user; keep logic minimal and only UI-related.
|
|
71
|
+
- Passes events to the business logic holder in response to user interactions.
|
|
72
|
+
- Keep views focused on presentation; extract reusable widgets into separate components.
|
|
73
|
+
- Use `StatelessWidget` when possible; keep build methods simple.
|
|
74
|
+
|
|
75
|
+
### Business Logic Holder (ViewModel / Cubit / Controller / Provider)
|
|
76
|
+
- Contains logic to convert app data into UI state and maintains current state needed by the view.
|
|
77
|
+
- Exposes callbacks (commands) to the View and retrieves/transforms data from repositories.
|
|
78
|
+
- Business logic must not live in UI widgets; only interact with repositories via the business logic holder.
|
|
79
|
+
|
|
80
|
+
### Repository
|
|
81
|
+
- Single Source of Truth (SSOT) for a given type of model data.
|
|
82
|
+
- The only class allowed to mutate its data; all other classes read from it.
|
|
83
|
+
- Handles business logic such as caching, error handling, and refreshing data.
|
|
84
|
+
- Transforms raw data from services into domain models consumed by business logic holders.
|
|
85
|
+
|
|
86
|
+
### Service
|
|
87
|
+
- Wraps API endpoints and exposes asynchronous response objects.
|
|
88
|
+
- Isolates data-loading and holds no state.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 4. Domain Layer (Use Cases)
|
|
93
|
+
|
|
94
|
+
Introduce use cases/interactors **only** when:
|
|
95
|
+
- Logic is complex or does not fit cleanly in the UI or Data layers.
|
|
96
|
+
- Logic is reused across multiple business logic holders or merges data from multiple repositories.
|
|
97
|
+
|
|
98
|
+
Do not add a domain layer for simple CRUD apps.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 5. Dependency Injection
|
|
103
|
+
|
|
104
|
+
Use dependency injection to provide components with their dependencies, enabling testability and flexibility.
|
|
105
|
+
|
|
106
|
+
- Supply repositories to business logic holders via constructors.
|
|
107
|
+
- Supply services to repositories via constructors.
|
|
108
|
+
- Design all components with well-defined interfaces so implementations can be swapped without changing consumers.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## References
|
|
113
|
+
|
|
114
|
+
- [Flutter app architecture guide](https://docs.flutter.dev/app-architecture/guide)
|
|
115
|
+
- [Architecture case study (Compass app)](https://docs.flutter.dev/app-architecture/case-study)
|
|
116
|
+
- [Architecture recommendations](https://docs.flutter.dev/app-architecture/recommendations)
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bloc
|
|
3
|
+
description: Implements Flutter state management using the bloc library (Bloc and Cubit). Use when creating new features, screens, or state management logic with bloc/cubit, modeling state, wiring Flutter widgets to blocs, or writing bloc/cubit unit tests.
|
|
4
|
+
routing: manual
|
|
5
|
+
stacks: [flutter]
|
|
6
|
+
source: https://github.com/evanca/flutter-ai-rules
|
|
7
|
+
license: MIT
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Bloc Skill
|
|
11
|
+
|
|
12
|
+
This skill defines how to design, implement, and test state management using the [bloc](https://pub.dev/packages/bloc) and [flutter_bloc](https://pub.dev/packages/flutter_bloc) libraries.
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
Use this skill when:
|
|
17
|
+
|
|
18
|
+
* Creating a new Cubit or Bloc for a feature.
|
|
19
|
+
* Modeling state (choosing between sealed classes vs a single state class).
|
|
20
|
+
* Wiring `BlocBuilder`, `BlocListener`, `BlocConsumer`, or `BlocProvider` in the widget tree.
|
|
21
|
+
* Writing unit tests for a Cubit or Bloc.
|
|
22
|
+
* Deciding between Cubit and Bloc.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 1. Cubit vs Bloc
|
|
27
|
+
|
|
28
|
+
| Situation | Use |
|
|
29
|
+
|---|---|
|
|
30
|
+
| Simple state, no events needed | `Cubit` |
|
|
31
|
+
| Complex flows, event traceability needed | `Bloc` |
|
|
32
|
+
| Advanced event processing (debounce, throttle) | `Bloc` with event transformers |
|
|
33
|
+
|
|
34
|
+
**Default to `Cubit`. Refactor to `Bloc` only when requirements grow.**
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 2. Naming Conventions
|
|
39
|
+
|
|
40
|
+
### Events (Bloc only)
|
|
41
|
+
- Named in **past tense**: `LoginButtonPressed`, `UserProfileLoaded`.
|
|
42
|
+
- Format: `BlocSubject` + optional noun + verb.
|
|
43
|
+
- Initial load event: `BlocSubjectStarted` (e.g., `AuthenticationStarted`).
|
|
44
|
+
- Base event class: `BlocSubjectEvent`.
|
|
45
|
+
|
|
46
|
+
### States
|
|
47
|
+
- Named as **nouns** (states are snapshots in time).
|
|
48
|
+
- Base state class: `BlocSubjectState`.
|
|
49
|
+
- Sealed subclasses: `BlocSubject` + `Initial` | `InProgress` | `Success` | `Failure`.
|
|
50
|
+
- Example: `LoginInitial`, `LoginInProgress`, `LoginSuccess`, `LoginFailure`.
|
|
51
|
+
- Single-class approach: `BlocSubjectState` + `BlocSubjectStatus` enum (`initial`, `loading`, `success`, `failure`).
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 3. Modeling State
|
|
56
|
+
|
|
57
|
+
### When to use a sealed class with subclasses
|
|
58
|
+
- States are **well-defined and mutually exclusive**.
|
|
59
|
+
- Type-safe exhaustive `switch` is desired.
|
|
60
|
+
- Subclass-specific properties exist.
|
|
61
|
+
|
|
62
|
+
```dart
|
|
63
|
+
@immutable
|
|
64
|
+
sealed class LoginState extends Equatable {
|
|
65
|
+
const LoginState();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
final class LoginInitial extends LoginState {
|
|
69
|
+
@override
|
|
70
|
+
List<Object?> get props => [];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
final class LoginInProgress extends LoginState {
|
|
74
|
+
@override
|
|
75
|
+
List<Object?> get props => [];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
final class LoginSuccess extends LoginState {
|
|
79
|
+
const LoginSuccess(this.user);
|
|
80
|
+
final User user;
|
|
81
|
+
@override
|
|
82
|
+
List<Object?> get props => [user];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
final class LoginFailure extends LoginState {
|
|
86
|
+
const LoginFailure(this.message);
|
|
87
|
+
final String message;
|
|
88
|
+
@override
|
|
89
|
+
List<Object?> get props => [message];
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Handle all states exhaustively in the UI:
|
|
94
|
+
```dart
|
|
95
|
+
switch (state) {
|
|
96
|
+
case LoginInitial(): ...
|
|
97
|
+
case LoginInProgress(): ...
|
|
98
|
+
case LoginSuccess(:final user): ...
|
|
99
|
+
case LoginFailure(:final message): ...
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### When to use a single class with a status enum
|
|
104
|
+
- Many shared properties across states.
|
|
105
|
+
- Simpler, more flexible; previous data must be retained after failure.
|
|
106
|
+
|
|
107
|
+
```dart
|
|
108
|
+
enum LoginStatus { initial, loading, success, failure }
|
|
109
|
+
|
|
110
|
+
@immutable
|
|
111
|
+
class LoginState extends Equatable {
|
|
112
|
+
const LoginState({
|
|
113
|
+
this.status = LoginStatus.initial,
|
|
114
|
+
this.user,
|
|
115
|
+
this.errorMessage,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
final LoginStatus status;
|
|
119
|
+
final User? user;
|
|
120
|
+
final String? errorMessage;
|
|
121
|
+
|
|
122
|
+
LoginState copyWith({
|
|
123
|
+
LoginStatus? status,
|
|
124
|
+
User? user,
|
|
125
|
+
String? errorMessage,
|
|
126
|
+
}) {
|
|
127
|
+
return LoginState(
|
|
128
|
+
status: status ?? this.status,
|
|
129
|
+
user: user ?? this.user,
|
|
130
|
+
errorMessage: errorMessage ?? this.errorMessage,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@override
|
|
135
|
+
List<Object?> get props => [status, user, errorMessage];
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### State rules (both approaches)
|
|
140
|
+
- Extend `Equatable` and pass all relevant fields to `props`.
|
|
141
|
+
- Copy `List`/`Map` properties with `List.of`/`Map.of` inside `props`.
|
|
142
|
+
- Annotate with `@immutable`.
|
|
143
|
+
- Always emit a **new instance**; never reuse the same state object.
|
|
144
|
+
- Duplicate states are ignored by bloc — ensure meaningful state changes.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 4. Cubit Implementation
|
|
149
|
+
|
|
150
|
+
```dart
|
|
151
|
+
class LoginCubit extends Cubit<LoginState> {
|
|
152
|
+
LoginCubit(this._authRepository) : super(const LoginState());
|
|
153
|
+
|
|
154
|
+
final AuthRepository _authRepository;
|
|
155
|
+
|
|
156
|
+
Future<void> login(String email, String password) async {
|
|
157
|
+
emit(state.copyWith(status: LoginStatus.loading));
|
|
158
|
+
try {
|
|
159
|
+
final user = await _authRepository.login(email, password);
|
|
160
|
+
emit(state.copyWith(status: LoginStatus.success, user: user));
|
|
161
|
+
} catch (e) {
|
|
162
|
+
emit(state.copyWith(status: LoginStatus.failure, errorMessage: e.toString()));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Rules:
|
|
169
|
+
- Only call `emit` inside the Cubit/Bloc.
|
|
170
|
+
- Public methods return `void` or `Future<void>` only.
|
|
171
|
+
- Keep business logic out of UI.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 5. Bloc Implementation
|
|
176
|
+
|
|
177
|
+
```dart
|
|
178
|
+
sealed class LoginEvent {}
|
|
179
|
+
final class LoginSubmitted extends LoginEvent {
|
|
180
|
+
LoginSubmitted({required this.email, required this.password});
|
|
181
|
+
final String email;
|
|
182
|
+
final String password;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
class LoginBloc extends Bloc<LoginEvent, LoginState> {
|
|
186
|
+
LoginBloc(this._authRepository) : super(LoginInitial()) {
|
|
187
|
+
on<LoginSubmitted>(_onLoginSubmitted);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
final AuthRepository _authRepository;
|
|
191
|
+
|
|
192
|
+
Future<void> _onLoginSubmitted(
|
|
193
|
+
LoginSubmitted event,
|
|
194
|
+
Emitter<LoginState> emit,
|
|
195
|
+
) async {
|
|
196
|
+
emit(LoginInProgress());
|
|
197
|
+
try {
|
|
198
|
+
final user = await _authRepository.login(event.email, event.password);
|
|
199
|
+
emit(LoginSuccess(user));
|
|
200
|
+
} catch (e) {
|
|
201
|
+
emit(LoginFailure(e.toString()));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Rules:
|
|
208
|
+
- Trigger state changes via `bloc.add(Event())`, not custom public methods.
|
|
209
|
+
- Keep event handler methods private (`_onEventName`).
|
|
210
|
+
- Internal/repository events must be private and may use custom transformers.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 6. Architecture
|
|
215
|
+
|
|
216
|
+
Three layers — each must stay in its own boundary:
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
Presentation → Business Logic (Cubit/Bloc) → Data (Repository → DataProvider)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- **Data Layer**: Repositories wrap data providers. Providers perform raw CRUD (HTTP, DB). Repositories expose clean domain objects.
|
|
223
|
+
- **Business Logic Layer**: Cubits/Blocs receive repository data and emit states. Inject repositories via constructor.
|
|
224
|
+
- **Presentation Layer**: Renders UI based on state. Handles user input by calling cubit methods or adding bloc events.
|
|
225
|
+
|
|
226
|
+
Rules:
|
|
227
|
+
- Blocs must not access data providers directly — only via repositories.
|
|
228
|
+
- No direct bloc-to-bloc communication. Use `BlocListener` in the UI to bridge blocs.
|
|
229
|
+
- For shared data, inject the same repository into multiple blocs.
|
|
230
|
+
- Initialize `BlocObserver` in `main.dart`.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## 7. Flutter Bloc Widgets
|
|
235
|
+
|
|
236
|
+
| Widget | Use |
|
|
237
|
+
|---|---|
|
|
238
|
+
| `BlocProvider` | Provide a bloc to a subtree |
|
|
239
|
+
| `MultiBlocProvider` | Provide multiple blocs without nesting |
|
|
240
|
+
| `BlocBuilder` | Rebuild UI on state change |
|
|
241
|
+
| `BlocListener` | Side effects only (navigation, dialogs, snackbars) |
|
|
242
|
+
| `MultiBlocListener` | Listen to multiple blocs without nesting |
|
|
243
|
+
| `BlocConsumer` | Rebuild UI + side effects together |
|
|
244
|
+
| `BlocSelector` | Rebuild only when a selected slice of state changes |
|
|
245
|
+
| `RepositoryProvider` | Provide a repository to the widget tree |
|
|
246
|
+
| `MultiRepositoryProvider` | Provide multiple repositories without nesting |
|
|
247
|
+
|
|
248
|
+
```dart
|
|
249
|
+
BlocProvider(
|
|
250
|
+
create: (context) => LoginCubit(context.read<AuthRepository>()),
|
|
251
|
+
child: LoginView(),
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
BlocBuilder<LoginCubit, LoginState>(
|
|
255
|
+
builder: (context, state) {
|
|
256
|
+
return switch (state.status) {
|
|
257
|
+
LoginStatus.loading => const CircularProgressIndicator(),
|
|
258
|
+
LoginStatus.success => const HomeView(),
|
|
259
|
+
LoginStatus.failure => Text(state.errorMessage ?? 'Error'),
|
|
260
|
+
LoginStatus.initial => const LoginForm(),
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
BlocListener<LoginCubit, LoginState>(
|
|
266
|
+
listener: (context, state) {
|
|
267
|
+
if (state.status == LoginStatus.failure) {
|
|
268
|
+
ScaffoldMessenger.of(context).showSnackBar(
|
|
269
|
+
SnackBar(content: Text(state.errorMessage ?? 'Login failed')),
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
child: LoginForm(),
|
|
274
|
+
);
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Rules:
|
|
278
|
+
- Use `context.read<T>()` in callbacks (not in `build`).
|
|
279
|
+
- Use `context.watch<T>()` in `build` only when necessary; prefer `BlocBuilder`.
|
|
280
|
+
- Never call `context.watch` or `context.select` at the root of `build` — scope with `Builder`.
|
|
281
|
+
- Handle **all** possible states in the UI (initial, loading, success, failure).
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## 8. Testing
|
|
286
|
+
|
|
287
|
+
Use `bloc_test` package. Mock repositories with `mocktail`.
|
|
288
|
+
|
|
289
|
+
```dart
|
|
290
|
+
import 'package:bloc_test/bloc_test.dart';
|
|
291
|
+
import 'package:mocktail/mocktail.dart';
|
|
292
|
+
import 'package:test/test.dart';
|
|
293
|
+
|
|
294
|
+
class MockAuthRepository extends Mock implements AuthRepository {}
|
|
295
|
+
|
|
296
|
+
void main() {
|
|
297
|
+
group('LoginCubit', () {
|
|
298
|
+
late AuthRepository authRepository;
|
|
299
|
+
late LoginCubit loginCubit;
|
|
300
|
+
|
|
301
|
+
setUp(() {
|
|
302
|
+
authRepository = MockAuthRepository();
|
|
303
|
+
loginCubit = LoginCubit(authRepository);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
tearDown(() => loginCubit.close());
|
|
307
|
+
|
|
308
|
+
test('initial state should be LoginState with status initial', () {
|
|
309
|
+
expect(loginCubit.state, const LoginState());
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
blocTest<LoginCubit, LoginState>(
|
|
313
|
+
'should emit [loading, success] when login succeeds',
|
|
314
|
+
build: () {
|
|
315
|
+
when(() => authRepository.login(any(), any()))
|
|
316
|
+
.thenAnswer((_) async => fakeUser);
|
|
317
|
+
return loginCubit;
|
|
318
|
+
},
|
|
319
|
+
act: (cubit) => cubit.login('email@test.com', 'password'),
|
|
320
|
+
expect: () => [
|
|
321
|
+
const LoginState(status: LoginStatus.loading),
|
|
322
|
+
LoginState(status: LoginStatus.success, user: fakeUser),
|
|
323
|
+
],
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
blocTest<LoginCubit, LoginState>(
|
|
327
|
+
'should emit [loading, failure] when login throws',
|
|
328
|
+
build: () {
|
|
329
|
+
when(() => authRepository.login(any(), any()))
|
|
330
|
+
.thenThrow(Exception('error'));
|
|
331
|
+
return loginCubit;
|
|
332
|
+
},
|
|
333
|
+
act: (cubit) => cubit.login('email@test.com', 'wrong'),
|
|
334
|
+
expect: () => [
|
|
335
|
+
const LoginState(status: LoginStatus.loading),
|
|
336
|
+
isA<LoginState>().having((s) => s.status, 'status', LoginStatus.failure),
|
|
337
|
+
],
|
|
338
|
+
);
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Rules:
|
|
344
|
+
- Always call `tearDown(() => cubit.close())`.
|
|
345
|
+
- Use `blocTest` for state emission assertions.
|
|
346
|
+
- Use `group()` named after the class under test.
|
|
347
|
+
- Name test cases with "should" to describe expected behavior.
|
|
348
|
+
- Register fallback values for custom types: `registerFallbackValue(MyEvent())`.
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## References
|
|
353
|
+
|
|
354
|
+
- [Bloc GitHub Repository](https://github.com/felangel/bloc)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-review
|
|
3
|
+
description: Performs thorough code reviews for Flutter/Dart pull requests and merge requests. Use when asked to review a PR, MR, branch, or a set of changed files. Follows a structured checklist covering correctness, security, style, testing, and documentation.
|
|
4
|
+
intents: [review]
|
|
5
|
+
stacks: [flutter]
|
|
6
|
+
source: https://github.com/evanca/flutter-ai-rules
|
|
7
|
+
license: MIT
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Code Review Skill
|
|
11
|
+
|
|
12
|
+
This skill defines how to perform structured, objective code reviews.
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
Use this skill when:
|
|
17
|
+
|
|
18
|
+
* Asked to review a pull request, merge request, or branch.
|
|
19
|
+
* Evaluating changed, added, or deleted files for correctness and quality.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Review Process
|
|
24
|
+
|
|
25
|
+
### 1. Branch and Merge Target
|
|
26
|
+
|
|
27
|
+
- Confirm the current branch is a **feature, bugfix, or PR/MR branch** — not the project's primary branch (e.g. `main`, `master`, `develop`, or equivalent).
|
|
28
|
+
- Verify the branch is **up-to-date** with the target branch.
|
|
29
|
+
- Identify the **target branch** for the merge.
|
|
30
|
+
|
|
31
|
+
### 2. Change Discovery
|
|
32
|
+
|
|
33
|
+
- List all **changed, added, and deleted files**.
|
|
34
|
+
- For each change, look up the **commit title** and review how connected components are implemented.
|
|
35
|
+
- **Never assume** a change or fix is correct without investigating the implementation details.
|
|
36
|
+
- If a change remains difficult to understand after several attempts, **note this explicitly** in your report.
|
|
37
|
+
|
|
38
|
+
### 3. Per-File Review
|
|
39
|
+
|
|
40
|
+
For every changed file, check:
|
|
41
|
+
|
|
42
|
+
| Area | What to verify |
|
|
43
|
+
|---|---|
|
|
44
|
+
| **Location** | File is in the correct directory |
|
|
45
|
+
| **Naming** | File name follows naming conventions |
|
|
46
|
+
| **Responsibility** | The file's responsibility is clear; reason for its change is understandable |
|
|
47
|
+
| **Readability** | Variable, function, and class names are descriptive and consistent |
|
|
48
|
+
| **Logic & correctness** | No logic errors or missing edge cases |
|
|
49
|
+
| **Maintainability** | Code is modular; no unnecessary duplication |
|
|
50
|
+
| **Error handling** | Errors and exceptions are handled appropriately |
|
|
51
|
+
| **Security** | No input validation issues; no secrets committed to code |
|
|
52
|
+
| **Performance** | No obvious performance issues or inefficiencies |
|
|
53
|
+
| **Documentation** | Public APIs, complex logic, and new modules are documented |
|
|
54
|
+
| **Test coverage** | New or changed logic has sufficient tests |
|
|
55
|
+
| **Style** | Code matches the project's style guide and coding patterns |
|
|
56
|
+
|
|
57
|
+
For **generated files**: confirm they are up-to-date and not manually modified.
|
|
58
|
+
|
|
59
|
+
### 4. Overall Change Set
|
|
60
|
+
|
|
61
|
+
- Verify the change set is **focused and scoped** to its stated purpose — no unrelated or unnecessary changes.
|
|
62
|
+
- Check that the **PR/MR description** accurately reflects the changes made.
|
|
63
|
+
- Confirm **new or updated tests** cover new or changed logic.
|
|
64
|
+
- Evaluate whether tests could **actually fail**, or only verify a mock implementation.
|
|
65
|
+
|
|
66
|
+
### 5. CI and Tests
|
|
67
|
+
|
|
68
|
+
- Ensure **all tests pass** in the continuous integration system.
|
|
69
|
+
- Fetch **online documentation** when unsure about best practices for a particular package or library.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Feedback Standards
|
|
74
|
+
|
|
75
|
+
- Be **objective and reasonable** — avoid automatic praise or flattery.
|
|
76
|
+
- Take a **devil's advocate approach**: give honest, thoughtful feedback.
|
|
77
|
+
- Provide **clear, constructive feedback** for any issues found, with suggestions for improvement.
|
|
78
|
+
- Include **requests for clarification** for anything that is unclear.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Output Format
|
|
83
|
+
|
|
84
|
+
Provide your review as a **chat response**, covering conclusions and recommendations **per file**, including:
|
|
85
|
+
|
|
86
|
+
1. Summary of what changed and why
|
|
87
|
+
2. Issues found (with severity: suggestion / minor / major)
|
|
88
|
+
3. Specific recommendations or questions per file
|
|
89
|
+
4. Overall verdict: approved, approved with suggestions, or changes requested
|