@the-bearded-bear/claude-craft 8.8.1 → 8.8.2
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/Dev/i18n/de/Angular/commands/check-compliance.md +218 -168
- package/Dev/i18n/de/Common/agents/refactoring-specialist.md +146 -69
- package/Dev/i18n/de/Common/commands/add-technology.md +166 -68
- package/Dev/i18n/de/Common/commands/setup-ci.md +216 -33
- package/Dev/i18n/de/Laravel/commands/check-compliance.md +223 -115
- package/Dev/i18n/de/Python/checklists/new-feature.md +319 -44
- package/Dev/i18n/de/React/rules/02-architecture.md +483 -322
- package/Dev/i18n/de/React/rules/03-coding-standards.md +584 -368
- package/Dev/i18n/de/Symfony/commands/check-compliance.md +186 -114
- package/Dev/i18n/de/Symfony/rules/07-testing-symfony.md +679 -10
- package/Dev/i18n/de/UIUX/commands/a11y-component.md +284 -26
- package/Dev/i18n/de/UIUX/commands/design-tokens.md +199 -35
- package/Dev/i18n/de/UIUX/commands/user-flow.md +179 -16
- package/Dev/i18n/de/VueJS/commands/check-compliance.md +227 -102
- package/Dev/i18n/es/Angular/commands/check-compliance.md +217 -167
- package/Dev/i18n/es/Common/commands/add-technology.md +158 -60
- package/Dev/i18n/es/Laravel/commands/check-compliance.md +223 -115
- package/Dev/i18n/es/React/commands/check-testing.md +263 -169
- package/Dev/i18n/es/ReactNative/commands/check-testing.md +256 -198
- package/Dev/i18n/es/Symfony/commands/check-compliance.md +189 -117
- package/Dev/i18n/es/Symfony/commands/check-testing.md +301 -102
- package/Dev/i18n/es/Symfony/commands/migration-plan.md +267 -34
- package/Dev/i18n/es/UIUX/commands/a11y-component.md +274 -16
- package/Dev/i18n/es/UIUX/commands/design-tokens.md +186 -22
- package/Dev/i18n/es/UIUX/commands/user-flow.md +173 -10
- package/Dev/i18n/es/VueJS/commands/check-compliance.md +227 -102
- package/Dev/i18n/fr/Angular/commands/check-compliance.md +217 -167
- package/Dev/i18n/fr/Common/commands/ralph-run.md +138 -60
- package/Dev/i18n/fr/Laravel/commands/check-compliance.md +223 -115
- package/Dev/i18n/fr/React/commands/check-architecture.md +370 -53
- package/Dev/i18n/fr/React/commands/check-code-quality.md +454 -64
- package/Dev/i18n/fr/React/commands/check-testing.md +476 -102
- package/Dev/i18n/fr/Symfony/commands/check-compliance.md +188 -116
- package/Dev/i18n/fr/VueJS/commands/check-compliance.md +227 -102
- package/Dev/i18n/pt/Angular/commands/check-compliance.md +225 -175
- package/Dev/i18n/pt/Common/agents/ralph-conductor.md +211 -30
- package/Dev/i18n/pt/Common/commands/add-technology.md +149 -51
- package/Dev/i18n/pt/Common/commands/ralph-run.md +239 -35
- package/Dev/i18n/pt/Flutter/commands/analyze-performance.md +331 -46
- package/Dev/i18n/pt/Flutter/commands/check-compliance.md +234 -29
- package/Dev/i18n/pt/Flutter/commands/generate-feature.md +526 -51
- package/Dev/i18n/pt/Flutter/commands/generate-widget.md +515 -27
- package/Dev/i18n/pt/Flutter/commands/golden-update.md +322 -46
- package/Dev/i18n/pt/Flutter/commands/localization-check.md +278 -37
- package/Dev/i18n/pt/Laravel/commands/check-compliance.md +212 -104
- package/Dev/i18n/pt/React/commands/accessibility-check.md +151 -42
- package/Dev/i18n/pt/React/commands/bundle-analyze.md +300 -25
- package/Dev/i18n/pt/React/commands/check-architecture.md +369 -34
- package/Dev/i18n/pt/React/commands/check-code-quality.md +440 -34
- package/Dev/i18n/pt/React/commands/check-compliance.md +232 -62
- package/Dev/i18n/pt/React/commands/check-security.md +370 -31
- package/Dev/i18n/pt/React/commands/check-testing.md +473 -43
- package/Dev/i18n/pt/React/commands/generate-component.md +454 -19
- package/Dev/i18n/pt/React/commands/generate-hook.md +425 -24
- package/Dev/i18n/pt/React/commands/storybook-story.md +520 -35
- package/Dev/i18n/pt/ReactNative/commands/app-size.md +358 -387
- package/Dev/i18n/pt/ReactNative/commands/check-code-quality.md +246 -75
- package/Dev/i18n/pt/ReactNative/commands/check-compliance.md +191 -85
- package/Dev/i18n/pt/ReactNative/commands/check-security.md +363 -115
- package/Dev/i18n/pt/ReactNative/commands/check-testing.md +269 -79
- package/Dev/i18n/pt/ReactNative/commands/generate-screen.md +491 -324
- package/Dev/i18n/pt/ReactNative/commands/native-module.md +539 -82
- package/Dev/i18n/pt/ReactNative/commands/store-prepare.md +429 -185
- package/Dev/i18n/pt/ReactNative/rules/02-architecture.md +1084 -31
- package/Dev/i18n/pt/Symfony/commands/check-compliance.md +195 -123
- package/Dev/i18n/pt/UIUX/commands/a11y-audit.md +208 -24
- package/Dev/i18n/pt/UIUX/commands/a11y-component.md +281 -23
- package/Dev/i18n/pt/UIUX/commands/design-tokens.md +197 -33
- package/Dev/i18n/pt/UIUX/commands/user-flow.md +183 -20
- package/Dev/i18n/pt/VueJS/commands/check-compliance.md +228 -103
- package/Dev/i18n/pt/Workflow/commands/analyze.md +147 -146
- package/Dev/i18n/pt/Workflow/commands/design.md +205 -204
- package/Dev/i18n/pt/Workflow/commands/implement.md +184 -184
- package/Project/i18n/de/Sprint/commands/dev.md +339 -86
- package/Project/i18n/es/Sprint/commands/dev.md +342 -91
- package/Project/i18n/pt/Sprint/commands/dev.md +343 -92
- package/bundles/cursor/.cursorrules +1 -1
- package/bundles/windsurf/.windsurfrules +1 -1
- package/package.json +1 -1
|
@@ -1,69 +1,544 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: Geração de Feature Flutter Completa
|
|
3
|
+
argument-hint: [arguments]
|
|
3
4
|
---
|
|
4
5
|
|
|
5
|
-
#
|
|
6
|
+
# Geração de Feature Flutter Completa
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
Você é um desenvolvedor Flutter sênior. Você deve gerar uma feature completa seguindo a Clean Architecture com BLoC/Riverpod, incluindo todos os arquivos necessários e os testes.
|
|
8
9
|
|
|
9
|
-
##
|
|
10
|
+
## Argumentos
|
|
11
|
+
$ARGUMENTS
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
13
|
+
Argumentos:
|
|
14
|
+
- Nome da feature (ex: `authentication`, `product_catalog`)
|
|
15
|
+
- (Opcional) Gerenciamento de estado (bloc, riverpod, cubit)
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
Exemplo: `/flutter:generate-feature authentication riverpod`
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
## Plan Mode
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
> **O modo de planejamento é obrigatório.** Antes de executar, o Claude ativa o modo de planejamento para analisar o código impactado, propor um plano de implementação e aguardar sua validação antes de realizar qualquer alteração.
|
|
20
22
|
|
|
23
|
+
## MISSÃO
|
|
24
|
+
|
|
25
|
+
### Etapa 1: Estrutura da Feature
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
lib/
|
|
29
|
+
└── features/
|
|
30
|
+
└── {feature}/
|
|
31
|
+
├── data/
|
|
32
|
+
│ ├── datasources/
|
|
33
|
+
│ │ ├── {feature}_local_datasource.dart
|
|
34
|
+
│ │ └── {feature}_remote_datasource.dart
|
|
35
|
+
│ ├── models/
|
|
36
|
+
│ │ └── {feature}_model.dart
|
|
37
|
+
│ └── repositories/
|
|
38
|
+
│ └── {feature}_repository_impl.dart
|
|
39
|
+
├── domain/
|
|
40
|
+
│ ├── entities/
|
|
41
|
+
│ │ └── {feature}_entity.dart
|
|
42
|
+
│ ├── repositories/
|
|
43
|
+
│ │ └── {feature}_repository.dart
|
|
44
|
+
│ └── usecases/
|
|
45
|
+
│ ├── get_{feature}.dart
|
|
46
|
+
│ └── create_{feature}.dart
|
|
47
|
+
└── presentation/
|
|
48
|
+
├── bloc/ (ou providers/)
|
|
49
|
+
│ ├── {feature}_bloc.dart
|
|
50
|
+
│ ├── {feature}_event.dart
|
|
51
|
+
│ └── {feature}_state.dart
|
|
52
|
+
├── pages/
|
|
53
|
+
│ ├── {feature}_page.dart
|
|
54
|
+
│ └── {feature}_detail_page.dart
|
|
55
|
+
└── widgets/
|
|
56
|
+
├── {feature}_list_item.dart
|
|
57
|
+
└── {feature}_form.dart
|
|
58
|
+
|
|
59
|
+
test/
|
|
60
|
+
└── features/
|
|
61
|
+
└── {feature}/
|
|
62
|
+
├── data/
|
|
63
|
+
│ └── repositories/
|
|
64
|
+
│ └── {feature}_repository_impl_test.dart
|
|
65
|
+
├── domain/
|
|
66
|
+
│ └── usecases/
|
|
67
|
+
│ └── get_{feature}_test.dart
|
|
68
|
+
└── presentation/
|
|
69
|
+
├── bloc/
|
|
70
|
+
│ └── {feature}_bloc_test.dart
|
|
71
|
+
└── pages/
|
|
72
|
+
└── {feature}_page_test.dart
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Etapa 2: Camada de Domínio
|
|
76
|
+
|
|
77
|
+
#### Entidade
|
|
78
|
+
```dart
|
|
79
|
+
// lib/features/{feature}/domain/entities/{feature}_entity.dart
|
|
80
|
+
import 'package:equatable/equatable.dart';
|
|
81
|
+
|
|
82
|
+
class {Feature}Entity extends Equatable {
|
|
83
|
+
final String id;
|
|
84
|
+
final String name;
|
|
85
|
+
final String? description;
|
|
86
|
+
final DateTime createdAt;
|
|
87
|
+
final DateTime? updatedAt;
|
|
88
|
+
|
|
89
|
+
const {Feature}Entity({
|
|
90
|
+
required this.id,
|
|
91
|
+
required this.name,
|
|
92
|
+
this.description,
|
|
93
|
+
required this.createdAt,
|
|
94
|
+
this.updatedAt,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
@override
|
|
98
|
+
List<Object?> get props => [id, name, description, createdAt, updatedAt];
|
|
99
|
+
|
|
100
|
+
{Feature}Entity copyWith({
|
|
101
|
+
String? id,
|
|
102
|
+
String? name,
|
|
103
|
+
String? description,
|
|
104
|
+
DateTime? createdAt,
|
|
105
|
+
DateTime? updatedAt,
|
|
106
|
+
}) {
|
|
107
|
+
return {Feature}Entity(
|
|
108
|
+
id: id ?? this.id,
|
|
109
|
+
name: name ?? this.name,
|
|
110
|
+
description: description ?? this.description,
|
|
111
|
+
createdAt: createdAt ?? this.createdAt,
|
|
112
|
+
updatedAt: updatedAt ?? this.updatedAt,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### Interface do Repositório
|
|
119
|
+
```dart
|
|
120
|
+
// lib/features/{feature}/domain/repositories/{feature}_repository.dart
|
|
121
|
+
import 'package:dartz/dartz.dart';
|
|
122
|
+
import '../entities/{feature}_entity.dart';
|
|
123
|
+
import '../../../../core/error/failures.dart';
|
|
124
|
+
|
|
125
|
+
abstract class {Feature}Repository {
|
|
126
|
+
Future<Either<Failure, List<{Feature}Entity>>> getAll();
|
|
127
|
+
Future<Either<Failure, {Feature}Entity>> getById(String id);
|
|
128
|
+
Future<Either<Failure, {Feature}Entity>> create({Feature}Entity entity);
|
|
129
|
+
Future<Either<Failure, {Feature}Entity>> update({Feature}Entity entity);
|
|
130
|
+
Future<Either<Failure, void>> delete(String id);
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### Casos de Uso
|
|
135
|
+
```dart
|
|
136
|
+
// lib/features/{feature}/domain/usecases/get_{feature}_list.dart
|
|
137
|
+
import 'package:dartz/dartz.dart';
|
|
138
|
+
import '../../../../core/error/failures.dart';
|
|
139
|
+
import '../../../../core/usecases/usecase.dart';
|
|
140
|
+
import '../entities/{feature}_entity.dart';
|
|
141
|
+
import '../repositories/{feature}_repository.dart';
|
|
142
|
+
|
|
143
|
+
class Get{Feature}List implements UseCase<List<{Feature}Entity>, NoParams> {
|
|
144
|
+
final {Feature}Repository repository;
|
|
145
|
+
|
|
146
|
+
Get{Feature}List(this.repository);
|
|
147
|
+
|
|
148
|
+
@override
|
|
149
|
+
Future<Either<Failure, List<{Feature}Entity>>> call(NoParams params) {
|
|
150
|
+
return repository.getAll();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Etapa 3: Camada de Dados
|
|
156
|
+
|
|
157
|
+
#### Modelo
|
|
158
|
+
```dart
|
|
159
|
+
// lib/features/{feature}/data/models/{feature}_model.dart
|
|
160
|
+
import '../../domain/entities/{feature}_entity.dart';
|
|
161
|
+
|
|
162
|
+
class {Feature}Model extends {Feature}Entity {
|
|
163
|
+
const {Feature}Model({
|
|
164
|
+
required super.id,
|
|
165
|
+
required super.name,
|
|
166
|
+
super.description,
|
|
167
|
+
required super.createdAt,
|
|
168
|
+
super.updatedAt,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
factory {Feature}Model.fromJson(Map<String, dynamic> json) {
|
|
172
|
+
return {Feature}Model(
|
|
173
|
+
id: json['id'] as String,
|
|
174
|
+
name: json['name'] as String,
|
|
175
|
+
description: json['description'] as String?,
|
|
176
|
+
createdAt: DateTime.parse(json['created_at'] as String),
|
|
177
|
+
updatedAt: json['updated_at'] != null
|
|
178
|
+
? DateTime.parse(json['updated_at'] as String)
|
|
179
|
+
: null,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
Map<String, dynamic> toJson() {
|
|
184
|
+
return {
|
|
185
|
+
'id': id,
|
|
186
|
+
'name': name,
|
|
187
|
+
'description': description,
|
|
188
|
+
'created_at': createdAt.toIso8601String(),
|
|
189
|
+
'updated_at': updatedAt?.toIso8601String(),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
factory {Feature}Model.fromEntity({Feature}Entity entity) {
|
|
194
|
+
return {Feature}Model(
|
|
195
|
+
id: entity.id,
|
|
196
|
+
name: entity.name,
|
|
197
|
+
description: entity.description,
|
|
198
|
+
createdAt: entity.createdAt,
|
|
199
|
+
updatedAt: entity.updatedAt,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
#### Implementação do Repositório
|
|
206
|
+
```dart
|
|
207
|
+
// lib/features/{feature}/data/repositories/{feature}_repository_impl.dart
|
|
208
|
+
import 'package:dartz/dartz.dart';
|
|
209
|
+
import '../../../../core/error/exceptions.dart';
|
|
210
|
+
import '../../../../core/error/failures.dart';
|
|
211
|
+
import '../../../../core/network/network_info.dart';
|
|
212
|
+
import '../../domain/entities/{feature}_entity.dart';
|
|
213
|
+
import '../../domain/repositories/{feature}_repository.dart';
|
|
214
|
+
import '../datasources/{feature}_local_datasource.dart';
|
|
215
|
+
import '../datasources/{feature}_remote_datasource.dart';
|
|
216
|
+
import '../models/{feature}_model.dart';
|
|
217
|
+
|
|
218
|
+
class {Feature}RepositoryImpl implements {Feature}Repository {
|
|
219
|
+
final {Feature}RemoteDataSource remoteDataSource;
|
|
220
|
+
final {Feature}LocalDataSource localDataSource;
|
|
221
|
+
final NetworkInfo networkInfo;
|
|
222
|
+
|
|
223
|
+
{Feature}RepositoryImpl({
|
|
224
|
+
required this.remoteDataSource,
|
|
225
|
+
required this.localDataSource,
|
|
226
|
+
required this.networkInfo,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
@override
|
|
230
|
+
Future<Either<Failure, List<{Feature}Entity>>> getAll() async {
|
|
231
|
+
if (await networkInfo.isConnected) {
|
|
232
|
+
try {
|
|
233
|
+
final remoteData = await remoteDataSource.getAll();
|
|
234
|
+
await localDataSource.cacheAll(remoteData);
|
|
235
|
+
return Right(remoteData);
|
|
236
|
+
} on ServerException {
|
|
237
|
+
return Left(ServerFailure());
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
try {
|
|
241
|
+
final localData = await localDataSource.getAll();
|
|
242
|
+
return Right(localData);
|
|
243
|
+
} on CacheException {
|
|
244
|
+
return Left(CacheFailure());
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Outros métodos...
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Etapa 4: Camada de Apresentação (BLoC)
|
|
254
|
+
|
|
255
|
+
#### BLoC
|
|
256
|
+
```dart
|
|
257
|
+
// lib/features/{feature}/presentation/bloc/{feature}_bloc.dart
|
|
258
|
+
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
259
|
+
import 'package:equatable/equatable.dart';
|
|
260
|
+
import '../../domain/entities/{feature}_entity.dart';
|
|
261
|
+
import '../../domain/usecases/get_{feature}_list.dart';
|
|
262
|
+
import '../../../../core/usecases/usecase.dart';
|
|
263
|
+
|
|
264
|
+
part '{feature}_event.dart';
|
|
265
|
+
part '{feature}_state.dart';
|
|
266
|
+
|
|
267
|
+
class {Feature}Bloc extends Bloc<{Feature}Event, {Feature}State> {
|
|
268
|
+
final Get{Feature}List get{Feature}List;
|
|
269
|
+
|
|
270
|
+
{Feature}Bloc({required this.get{Feature}List}) : super(const {Feature}Initial()) {
|
|
271
|
+
on<Load{Feature}List>(_onLoad{Feature}List);
|
|
272
|
+
on<Refresh{Feature}List>(_onRefresh{Feature}List);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
Future<void> _onLoad{Feature}List(
|
|
276
|
+
Load{Feature}List event,
|
|
277
|
+
Emitter<{Feature}State> emit,
|
|
278
|
+
) async {
|
|
279
|
+
emit(const {Feature}Loading());
|
|
280
|
+
|
|
281
|
+
final result = await get{Feature}List(NoParams());
|
|
282
|
+
|
|
283
|
+
result.fold(
|
|
284
|
+
(failure) => emit({Feature}Error(message: failure.message)),
|
|
285
|
+
(data) => emit({Feature}Loaded(items: data)),
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
Future<void> _onRefresh{Feature}List(
|
|
290
|
+
Refresh{Feature}List event,
|
|
291
|
+
Emitter<{Feature}State> emit,
|
|
292
|
+
) async {
|
|
293
|
+
final result = await get{Feature}List(NoParams());
|
|
294
|
+
|
|
295
|
+
result.fold(
|
|
296
|
+
(failure) => emit({Feature}Error(message: failure.message)),
|
|
297
|
+
(data) => emit({Feature}Loaded(items: data)),
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
#### Eventos
|
|
304
|
+
```dart
|
|
305
|
+
// lib/features/{feature}/presentation/bloc/{feature}_event.dart
|
|
306
|
+
part of '{feature}_bloc.dart';
|
|
307
|
+
|
|
308
|
+
abstract class {Feature}Event extends Equatable {
|
|
309
|
+
const {Feature}Event();
|
|
310
|
+
|
|
311
|
+
@override
|
|
312
|
+
List<Object> get props => [];
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
class Load{Feature}List extends {Feature}Event {
|
|
316
|
+
const Load{Feature}List();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
class Refresh{Feature}List extends {Feature}Event {
|
|
320
|
+
const Refresh{Feature}List();
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
#### Estados
|
|
325
|
+
```dart
|
|
326
|
+
// lib/features/{feature}/presentation/bloc/{feature}_state.dart
|
|
327
|
+
part of '{feature}_bloc.dart';
|
|
328
|
+
|
|
329
|
+
abstract class {Feature}State extends Equatable {
|
|
330
|
+
const {Feature}State();
|
|
331
|
+
|
|
332
|
+
@override
|
|
333
|
+
List<Object?> get props => [];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
class {Feature}Initial extends {Feature}State {
|
|
337
|
+
const {Feature}Initial();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
class {Feature}Loading extends {Feature}State {
|
|
341
|
+
const {Feature}Loading();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
class {Feature}Loaded extends {Feature}State {
|
|
345
|
+
final List<{Feature}Entity> items;
|
|
346
|
+
|
|
347
|
+
const {Feature}Loaded({required this.items});
|
|
348
|
+
|
|
349
|
+
@override
|
|
350
|
+
List<Object?> get props => [items];
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
class {Feature}Error extends {Feature}State {
|
|
354
|
+
final String message;
|
|
355
|
+
|
|
356
|
+
const {Feature}Error({required this.message});
|
|
357
|
+
|
|
358
|
+
@override
|
|
359
|
+
List<Object?> get props => [message];
|
|
360
|
+
}
|
|
21
361
|
```
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
362
|
+
|
|
363
|
+
### Etapa 5: Página
|
|
364
|
+
|
|
365
|
+
```dart
|
|
366
|
+
// lib/features/{feature}/presentation/pages/{feature}_page.dart
|
|
367
|
+
import 'package:flutter/material.dart';
|
|
368
|
+
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
369
|
+
import '../bloc/{feature}_bloc.dart';
|
|
370
|
+
import '../widgets/{feature}_list_item.dart';
|
|
371
|
+
|
|
372
|
+
class {Feature}Page extends StatelessWidget {
|
|
373
|
+
const {Feature}Page({super.key});
|
|
374
|
+
|
|
375
|
+
static const routeName = '/{feature}';
|
|
376
|
+
|
|
377
|
+
@override
|
|
378
|
+
Widget build(BuildContext context) {
|
|
379
|
+
return Scaffold(
|
|
380
|
+
appBar: AppBar(
|
|
381
|
+
title: const Text('{Feature}'),
|
|
382
|
+
actions: [
|
|
383
|
+
IconButton(
|
|
384
|
+
icon: const Icon(Icons.refresh),
|
|
385
|
+
onPressed: () {
|
|
386
|
+
context.read<{Feature}Bloc>().add(const Refresh{Feature}List());
|
|
387
|
+
},
|
|
388
|
+
),
|
|
389
|
+
],
|
|
390
|
+
),
|
|
391
|
+
body: BlocBuilder<{Feature}Bloc, {Feature}State>(
|
|
392
|
+
builder: (context, state) {
|
|
393
|
+
return switch (state) {
|
|
394
|
+
{Feature}Initial() => const Center(
|
|
395
|
+
child: Text('Pressione refresh'),
|
|
396
|
+
),
|
|
397
|
+
{Feature}Loading() => const Center(
|
|
398
|
+
child: CircularProgressIndicator(),
|
|
399
|
+
),
|
|
400
|
+
{Feature}Loaded(:final items) => items.isEmpty
|
|
401
|
+
? const Center(child: Text('Nenhum item'))
|
|
402
|
+
: RefreshIndicator(
|
|
403
|
+
onRefresh: () async {
|
|
404
|
+
context
|
|
405
|
+
.read<{Feature}Bloc>()
|
|
406
|
+
.add(const Refresh{Feature}List());
|
|
407
|
+
},
|
|
408
|
+
child: ListView.builder(
|
|
409
|
+
itemCount: items.length,
|
|
410
|
+
itemBuilder: (context, index) {
|
|
411
|
+
return {Feature}ListItem(item: items[index]);
|
|
412
|
+
},
|
|
413
|
+
),
|
|
414
|
+
),
|
|
415
|
+
{Feature}Error(:final message) => Center(
|
|
416
|
+
child: Column(
|
|
417
|
+
mainAxisAlignment: MainAxisAlignment.center,
|
|
418
|
+
children: [
|
|
419
|
+
Text('Erro: $message'),
|
|
420
|
+
const SizedBox(height: 16),
|
|
421
|
+
ElevatedButton(
|
|
422
|
+
onPressed: () {
|
|
423
|
+
context
|
|
424
|
+
.read<{Feature}Bloc>()
|
|
425
|
+
.add(const Load{Feature}List());
|
|
426
|
+
},
|
|
427
|
+
child: const Text('Tentar novamente'),
|
|
428
|
+
),
|
|
429
|
+
],
|
|
430
|
+
),
|
|
431
|
+
),
|
|
432
|
+
};
|
|
433
|
+
},
|
|
434
|
+
),
|
|
435
|
+
floatingActionButton: FloatingActionButton(
|
|
436
|
+
onPressed: () {
|
|
437
|
+
// Navegação para criação
|
|
438
|
+
},
|
|
439
|
+
child: const Icon(Icons.add),
|
|
440
|
+
),
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
47
444
|
```
|
|
48
445
|
|
|
49
|
-
|
|
446
|
+
### Etapa 6: Testes
|
|
447
|
+
|
|
448
|
+
```dart
|
|
449
|
+
// test/features/{feature}/presentation/bloc/{feature}_bloc_test.dart
|
|
450
|
+
import 'package:bloc_test/bloc_test.dart';
|
|
451
|
+
import 'package:dartz/dartz.dart';
|
|
452
|
+
import 'package:flutter_test/flutter_test.dart';
|
|
453
|
+
import 'package:mocktail/mocktail.dart';
|
|
454
|
+
|
|
455
|
+
class MockGet{Feature}List extends Mock implements Get{Feature}List {}
|
|
456
|
+
|
|
457
|
+
void main() {
|
|
458
|
+
late {Feature}Bloc bloc;
|
|
459
|
+
late MockGet{Feature}List mockGet{Feature}List;
|
|
460
|
+
|
|
461
|
+
setUp(() {
|
|
462
|
+
mockGet{Feature}List = MockGet{Feature}List();
|
|
463
|
+
bloc = {Feature}Bloc(get{Feature}List: mockGet{Feature}List);
|
|
464
|
+
});
|
|
50
465
|
|
|
466
|
+
tearDown(() {
|
|
467
|
+
bloc.close();
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
test('initial state should be {Feature}Initial', () {
|
|
471
|
+
expect(bloc.state, const {Feature}Initial());
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
blocTest<{Feature}Bloc, {Feature}State>(
|
|
475
|
+
'emits [Loading, Loaded] when Load{Feature}List is added successfully',
|
|
476
|
+
build: () {
|
|
477
|
+
when(() => mockGet{Feature}List(any()))
|
|
478
|
+
.thenAnswer((_) async => const Right([]));
|
|
479
|
+
return bloc;
|
|
480
|
+
},
|
|
481
|
+
act: (bloc) => bloc.add(const Load{Feature}List()),
|
|
482
|
+
expect: () => [
|
|
483
|
+
const {Feature}Loading(),
|
|
484
|
+
const {Feature}Loaded(items: []),
|
|
485
|
+
],
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
blocTest<{Feature}Bloc, {Feature}State>(
|
|
489
|
+
'emits [Loading, Error] when Load{Feature}List fails',
|
|
490
|
+
build: () {
|
|
491
|
+
when(() => mockGet{Feature}List(any()))
|
|
492
|
+
.thenAnswer((_) async => Left(ServerFailure()));
|
|
493
|
+
return bloc;
|
|
494
|
+
},
|
|
495
|
+
act: (bloc) => bloc.add(const Load{Feature}List()),
|
|
496
|
+
expect: () => [
|
|
497
|
+
const {Feature}Loading(),
|
|
498
|
+
isA<{Feature}Error>(),
|
|
499
|
+
],
|
|
500
|
+
);
|
|
501
|
+
}
|
|
51
502
|
```
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
repositories/{feature}_repository_impl_test.dart
|
|
56
|
-
domain/
|
|
57
|
-
usecases/get_{feature}_test.dart
|
|
58
|
-
presentation/
|
|
59
|
-
bloc/{feature}_bloc_test.dart
|
|
60
|
-
widgets/{feature}_widget_test.dart
|
|
503
|
+
|
|
504
|
+
### Etapa 7: Resumo
|
|
505
|
+
|
|
61
506
|
```
|
|
507
|
+
══════════════════════════════════════════════════════════════
|
|
508
|
+
✅ FEATURE GERADA - {feature}
|
|
509
|
+
══════════════════════════════════════════════════════════════
|
|
62
510
|
|
|
63
|
-
|
|
511
|
+
📁 Arquivos criados:
|
|
64
512
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
513
|
+
Camada de Domínio (4 arquivos):
|
|
514
|
+
- lib/features/{feature}/domain/entities/{feature}_entity.dart
|
|
515
|
+
- lib/features/{feature}/domain/repositories/{feature}_repository.dart
|
|
516
|
+
- lib/features/{feature}/domain/usecases/get_{feature}_list.dart
|
|
517
|
+
- lib/features/{feature}/domain/usecases/create_{feature}.dart
|
|
518
|
+
|
|
519
|
+
Camada de Dados (4 arquivos):
|
|
520
|
+
- lib/features/{feature}/data/models/{feature}_model.dart
|
|
521
|
+
- lib/features/{feature}/data/repositories/{feature}_repository_impl.dart
|
|
522
|
+
- lib/features/{feature}/data/datasources/{feature}_remote_datasource.dart
|
|
523
|
+
- lib/features/{feature}/data/datasources/{feature}_local_datasource.dart
|
|
524
|
+
|
|
525
|
+
Camada de Apresentação (6 arquivos):
|
|
526
|
+
- lib/features/{feature}/presentation/bloc/{feature}_bloc.dart
|
|
527
|
+
- lib/features/{feature}/presentation/bloc/{feature}_event.dart
|
|
528
|
+
- lib/features/{feature}/presentation/bloc/{feature}_state.dart
|
|
529
|
+
- lib/features/{feature}/presentation/pages/{feature}_page.dart
|
|
530
|
+
- lib/features/{feature}/presentation/widgets/{feature}_list_item.dart
|
|
531
|
+
- lib/features/{feature}/presentation/widgets/{feature}_form.dart
|
|
532
|
+
|
|
533
|
+
Testes (4 arquivos):
|
|
534
|
+
- test/features/{feature}/domain/usecases/get_{feature}_list_test.dart
|
|
535
|
+
- test/features/{feature}/data/repositories/{feature}_repository_impl_test.dart
|
|
536
|
+
- test/features/{feature}/presentation/bloc/{feature}_bloc_test.dart
|
|
537
|
+
- test/features/{feature}/presentation/pages/{feature}_page_test.dart
|
|
538
|
+
|
|
539
|
+
🔧 Próximas etapas:
|
|
540
|
+
1. Configurar a injeção de dependências (get_it/riverpod)
|
|
541
|
+
2. Adicionar as rotas
|
|
542
|
+
3. Implementar os datasources
|
|
543
|
+
4. Executar os testes: flutter test
|
|
544
|
+
```
|