UNPKG

appiq-solution

Version:

APPIQ SOLUTION: Flutter Mobile Development Extension for BMAD Method

617 lines (517 loc) β€’ 18.4 kB
# Flutter Clean Architecture Development Guidelines ## πŸ“‹ Project Overview This guide establishes comprehensive Flutter development standards following Clean Architecture principles, ensuring scalable, testable, and maintainable mobile applications. ## πŸ—οΈ Architecture Overview ### Clean Architecture Layers ``` lib/ β”œβ”€β”€ core/ # Shared Core Functionality β”‚ β”œβ”€β”€ constants/ # API endpoints, app constants β”‚ β”œβ”€β”€ di/ # Dependency injection setup β”‚ β”œβ”€β”€ error/ # Error handling (failures, exceptions) β”‚ β”œβ”€β”€ network/ # Networking setup (API client, network info) β”‚ β”œβ”€β”€ local_storage/ # Hive/SharedPrefs service for local storage β”‚ └── utils/ # Core utilities and helpers β”œβ”€β”€ features/ # Feature-based modules β”‚ └── [feature_name]/ # Individual features β”‚ β”œβ”€β”€ data/ # Data layer β”‚ β”‚ β”œβ”€β”€ datasources/ # Remote/Local data sources β”‚ β”‚ β”œβ”€β”€ models/ # Data models with JSON serialization β”‚ β”‚ └── repositories/ # Repository implementations β”‚ β”œβ”€β”€ domain/ # Domain layer (Business Logic) β”‚ β”‚ β”œβ”€β”€ entities/ # Business entities β”‚ β”‚ β”œβ”€β”€ repositories/ # Repository interfaces β”‚ β”‚ └── usecases/ # Business use cases β”‚ └── presentation/ # Presentation layer (UI) β”‚ β”œβ”€β”€ cubit/ # State management (Cubit + State) β”‚ β”œβ”€β”€ pages/ # UI pages/screens β”‚ └── widgets/ # Feature-specific widgets β”œβ”€β”€ shared/ # Shared UI components and utilities β”‚ β”œβ”€β”€ theme/ # Theme management β”‚ β”œβ”€β”€ widgets/ # Reusable widgets (BaseScaffold, etc.) β”‚ β”œβ”€β”€ constants/ # UI constants β”‚ └── utils/ # Shared utilities └── l10n/ # Localization files (.arb) ``` ## 🎯 Development Patterns & Style Guide ### 1. Entity Pattern (Domain Layer) **Struktur:** - Extends `Equatable` fΓΌr Value Equality - Immutable mit `const` Konstruktoren - `copyWith` Methoden fΓΌr Updates - Alle Properties in `props` Liste ```dart class UserEntity extends Equatable { const UserEntity({ required this.id, required this.name, required this.email, this.bio, this.avatarUrl, this.createdAt, this.updatedAt, }); final String id; final String name; final String email; final String? bio; final String? avatarUrl; final DateTime? createdAt; final DateTime? updatedAt; @override List<Object?> get props => [ id, name, email, bio, avatarUrl, createdAt, updatedAt, ]; UserEntity copyWith({ String? id, String? name, String? email, String? bio, String? avatarUrl, DateTime? createdAt, DateTime? updatedAt, }) { return UserEntity( id: id ?? this.id, name: name ?? this.name, email: email ?? this.email, bio: bio ?? this.bio, avatarUrl: avatarUrl ?? this.avatarUrl, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, ); } } ``` ### 2. Model Pattern (Data Layer) **Struktur:** - `@freezed` Annotation fΓΌr Immutability - JSON Serialization mit `json_serializable` - `@JsonKey` fΓΌr API Field Mapping - Extension Methods fΓΌr Entity Conversion ```dart import 'package:freezed_annotation/freezed_annotation.dart'; import '../../domain/entities/user_entity.dart'; part 'user_model.freezed.dart'; part 'user_model.g.dart'; @freezed class UserModel with _$UserModel { const factory UserModel({ required String id, required String name, required String email, String? bio, @JsonKey(name: 'avatar_url') String? avatarUrl, @JsonKey(name: 'created_at') String? createdAt, @JsonKey(name: 'updated_at') String? updatedAt, }) = _UserModel; factory UserModel.fromJson(Map<String, dynamic> json) => _$UserModelFromJson(json); } extension UserModelExtension on UserModel { UserEntity toEntity() { return UserEntity( id: id, name: name, email: email, bio: bio, avatarUrl: avatarUrl, createdAt: createdAt != null ? DateTime.tryParse(createdAt!) : null, updatedAt: updatedAt != null ? DateTime.tryParse(updatedAt!) : null, ); } } ``` ### 3. Repository Pattern **Interface (Domain Layer):** ```dart import 'package:dartz/dartz.dart'; import '../../../../core/error/failures.dart'; import '../entities/user_entity.dart'; abstract class UserRepository { Future<Either<Failure, List<UserEntity>>> getUsers(); Future<Either<Failure, UserEntity>> getUserById(String id); Future<Either<Failure, UserEntity>> createUser(UserEntity user); Future<Either<Failure, UserEntity>> updateUser(UserEntity user); Future<Either<Failure, void>> deleteUser(String id); Future<Either<Failure, List<UserEntity>>> searchUsers(String query); } ``` ### 4. UseCase Pattern **Struktur:** - `@injectable` fΓΌr Dependency Injection - `call` Methode als Main Entry Point - Business Logic & Validation - `Either<Failure, Success>` Return Type ```dart import 'package:dartz/dartz.dart'; import 'package:injectable/injectable.dart'; import '../../../../core/error/failures.dart'; import '../entities/user_entity.dart'; import '../repositories/user_repository.dart'; @injectable class CreateUserUseCase { const CreateUserUseCase(this._repository); final UserRepository _repository; Future<Either<Failure, UserEntity>> call(UserEntity user) async { // Validate user data final validationResult = _validateUser(user); if (validationResult != null) { return Left(ValidationFailure(validationResult)); } return await _repository.createUser(user); } String? _validateUser(UserEntity user) { if (user.name.trim().isEmpty) { return 'Name is required'; } if (user.email.trim().isEmpty) { return 'Email is required'; } if (!_isValidEmail(user.email)) { return 'Please enter a valid email address'; } return null; } bool _isValidEmail(String email) { return RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(email); } } ``` ### 5. State Management Pattern (Cubit) **State Class:** ```dart import 'package:equatable/equatable.dart'; import '../../domain/entities/user_entity.dart'; enum UserStatus { initial, loading, success, failure, } class UserState extends Equatable { const UserState({ this.status = UserStatus.initial, this.users = const [], this.selectedUser, this.message, }); final UserStatus status; final List<UserEntity> users; final UserEntity? selectedUser; final String? message; UserState copyWith({ UserStatus? status, List<UserEntity>? users, UserEntity? selectedUser, String? message, }) { return UserState( status: status ?? this.status, users: users ?? this.users, selectedUser: selectedUser ?? this.selectedUser, message: message ?? this.message, ); } @override List<Object?> get props => [status, users, selectedUser, message]; } ``` **Cubit Implementation:** ```dart import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:injectable/injectable.dart'; import '../../domain/entities/user_entity.dart'; import '../../domain/usecases/get_users_usecase.dart'; import 'user_state.dart'; @injectable class UserCubit extends Cubit<UserState> { UserCubit( this._getUsersUseCase, this._createUserUseCase, this._updateUserUseCase, this._deleteUserUseCase, ) : super(const UserState()); final GetUsersUseCase _getUsersUseCase; final CreateUserUseCase _createUserUseCase; final UpdateUserUseCase _updateUserUseCase; final DeleteUserUseCase _deleteUserUseCase; Future<void> getUsers() async { emit(state.copyWith(status: UserStatus.loading)); final result = await _getUsersUseCase(); result.fold( (failure) => emit(state.copyWith( status: UserStatus.failure, message: failure.message ?? 'Failed to load users', )), (users) => emit(state.copyWith( status: UserStatus.success, users: users, )), ); } Future<void> createUser(UserEntity user) async { emit(state.copyWith(status: UserStatus.loading)); final result = await _createUserUseCase(user); result.fold( (failure) => emit(state.copyWith( status: UserStatus.failure, message: failure.message ?? 'Failed to create user', )), (createdUser) { final updatedUsers = [...state.users, createdUser]; emit(state.copyWith( status: UserStatus.success, users: updatedUsers, )); }, ); } } ``` ## πŸ› οΈ Technology Stack ### Core Dependencies - **State Management**: `flutter_bloc` ^8.1.6 - **Dependency Injection**: `get_it` ^8.0.2 + `injectable` ^2.5.0 - **Code Generation**: `freezed` ^2.5.7 + `json_serializable` ^6.8.0 - **Networking**: `dio` ^5.7.0 - **Local Storage**: `hive` ^2.2.3 - **Error Handling**: `dartz` ^0.10.1 - **Value Equality**: `equatable` ^2.0.7 - **Routing**: `go_router` ^14.0.0 - **Localization**: `flutter_localizations` + `intl` ^0.19.0 ### Dev Dependencies - **Build Runner**: `build_runner` ^2.4.13 - **Code Generation**: `injectable_generator` ^2.6.2 - **Testing**: `bloc_test` ^9.1.7 + `mocktail` ^1.0.4 ## 🌐 Multi-Language Support ### Localization Setup ```dart // l10n.yaml arb-dir: lib/l10n template-arb-file: app_en.arb output-localization-file: app_localizations.dart output-class: AppLocalizations ``` ### ARB File Structure ```json // app_en.arb { "@@locale": "en", "appTitle": "My App", "@appTitle": { "description": "The title of the application" }, "welcomeMessage": "Welcome, {userName}!", "@welcomeMessage": { "description": "Welcome message with user name", "placeholders": { "userName": { "type": "String", "example": "John" } } } } ``` ### Usage in Widgets ```dart import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; return Text(l10n.welcomeMessage('John')); } } ``` ## 🎨 UI/UX Patterns ### Shared Components **BaseScaffold - Responsive Layout:** ```dart class BaseScaffold extends StatelessWidget { const BaseScaffold({ super.key, required this.body, this.title, this.actions, this.floatingActionButton, this.safeArea = true, }); final Widget body; final String? title; final List<Widget>? actions; final Widget? floatingActionButton; final bool safeArea; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final mediaQuery = MediaQuery.of(context); final isTablet = mediaQuery.size.width >= 768; Widget bodyWidget = body; // Responsive constraints if (isTablet) { bodyWidget = Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 800), child: bodyWidget, ), ); } return Scaffold( appBar: title != null ? AppBar( title: Text(title!), actions: actions, ) : null, body: safeArea ? SafeArea(child: bodyWidget) : bodyWidget, floatingActionButton: floatingActionButton, ); } } ``` ## πŸ”§ Development Commands ### Code Generation ```bash # Generate all code (models, DI, etc.) flutter packages pub run build_runner build --delete-conflicting-outputs # Generate localization files flutter gen-l10n # Clean and rebuild flutter clean && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs ``` ### Testing ```bash # Run all tests flutter test # Run with coverage flutter test --coverage # Run integration tests flutter test integration_test/ ``` ## πŸ“‹ Naming Conventions ### Files & Directories - **Files**: `snake_case.dart` - **Directories**: `snake_case/` - **Assets**: `snake_case.png` ### Code - **Classes**: `PascalCase` - **Variables & Methods**: `camelCase` - **Constants**: `SCREAMING_SNAKE_CASE` in static classes - **Private Members**: `_leadingUnderscore` ### Feature Naming Pattern - **Entity**: `FeatureEntity` - **Model**: `FeatureModel` - **Repository Interface**: `FeatureRepository` - **Repository Implementation**: `FeatureRepositoryImpl` - **DataSource Interface**: `FeatureRemoteDataSource` - **DataSource Implementation**: `FeatureRemoteDataSourceImpl` - **UseCase**: `GetFeatureUseCase`, `CreateFeatureUseCase`, etc. - **Cubit**: `FeatureCubit` - **State**: `FeatureState` - **Page**: `FeatureListPage`, `FeatureDetailPage` ## 🎯 Development Guidelines ### Best Practices 1. **Immutability**: Alle Data Classes sind immutable 2. **Single Responsibility**: Jede Klasse hat eine einzige Verantwortung 3. **Dependency Injection**: Alle Dependencies werden injected 4. **Error Handling**: Consistent Either<Failure, Success> Pattern 5. **Testing**: Jeder Use Case und Repository wird getestet 6. **Documentation**: Alle Public APIs sind dokumentiert 7. **Localization**: Keine statischen Texte, immer l10n verwenden 8. **Security**: Input validation und sichere Datenspeicherung ### Code Quality Checks - **Linting**: `flutter_lints` fΓΌr Code-QualitΓ€t - **Type Safety**: Null Safety aktiviert - **Build Runner**: RegelmÀßige Code-Generierung - **Testing**: Unit Tests fΓΌr Business Logic - **Security**: RegelmÀßige SicherheitsprΓΌfungen ## πŸš€ Standard Workflow ### Development Process 1. **Think through the problem** - Analyze requirements and read codebase 2. **Write a plan** to tasks/todo.md with checkable items 3. **Get plan verified** before beginning work 4. **Work on todo items** marking them complete as you go 5. **Provide high-level explanations** of changes made 6. **Keep changes simple** - impact as little code as possible 7. **Add review section** to todo.md with summary 8. **Make git commits** after each fully finished task ### Quality Criteria - **Architecture**: Follows Clean Architecture with proper layer separation - **Localization**: All text supports multi-language - **Quality**: Passes all 5 quality criteria (DRY, Readable, Maintainable, Performant, Testable) - **Documentation**: Code is properly documented with examples - **Consistency**: Code style matches existing patterns ## πŸ”’ Security Guidelines ### Input Validation - Validate all user inputs at UI and business logic level - Use proper form validation with error messages - Sanitize data before API calls ### Data Storage - Use secure storage for sensitive data (flutter_secure_storage) - Never store passwords in plain text - Encrypt sensitive local data ### Network Security - Use HTTPS for all API calls - Implement certificate pinning for production - Validate SSL certificates ### Authentication - Implement proper token management - Use secure token storage - Handle token refresh properly ## 🚨 MANDATORY DEVELOPMENT RULES - ALWAYS FOLLOW! ### CRITICAL: These rules MUST be followed by ALL agents, ALL workflows, ALL IDEs! #### Standard Workflow (NO EXCEPTIONS!) 1. **THINK**: Analyze problem and read existing codebase 2. **PLAN**: Write detailed plan to `tasks/todo.md` with checkable items 3. **VERIFY**: Get plan approved before starting ANY coding 4. **WORK**: Execute todo items, marking complete as you go 5. **EXPLAIN**: Provide high-level explanations at every step 6. **SIMPLE**: Make minimal impact changes - avoid complexity 7. **REVIEW**: Add review section to `todo.md` with summary 8. **COMMIT**: Make git commit after each completed task #### MANDATORY Quality Gates (ALL 5 MUST PASS!) - 🧹 **DRY**: No code duplication - ALWAYS reuse existing code - πŸ“– **Readable**: Self-documenting code with clear naming - πŸ”§ **Maintainable**: Clean Architecture with proper separation - ⚑ **Performant**: Efficient algorithms and resource usage - πŸ§ͺ **Testable**: Comprehensive test coverage #### MANDATORY Pre-Coding Checklist - [ ] **Check existing code** for similar functionality FIRST - [ ] **Review shared components** in `lib/shared/` - [ ] **Follow existing patterns** - NEVER create new patterns - [ ] **Plan localization keys** - NO static text allowed - [ ] **Verify architecture compliance** with Clean Architecture - [ ] **Identify reusable components** before creating new ones #### MANDATORY Flutter Rules - βœ… **Clean Architecture**: Presentation β†’ Domain ← Data - βœ… **Cubit State Management**: Follow existing Cubit patterns - βœ… **Repository Pattern**: Implement like existing repositories - βœ… **Dependency Injection**: Use GetIt like existing features - βœ… **Localization**: ALL text must use AppLocalizations - βœ… **Testing**: Unit tests for business logic, widget tests for UI #### MANDATORY Code Integration Rules - βœ… **BEFORE new files**: Check if similar functionality exists - βœ… **BEFORE new widgets**: Review `lib/shared/widgets/` - βœ… **BEFORE new services**: Check `lib/shared/services/` - βœ… **BEFORE new utilities**: Review `lib/shared/utils/` - βœ… **EXTEND existing code** instead of duplicating - βœ… **FOLLOW existing naming conventions** exactly #### MANDATORY Validation Criteria All code MUST pass these criteria: 1. **Architecture**: Follows Clean Architecture with proper layer separation 2. **Localization**: All text supports multi-language (NO static text!) 3. **Quality**: Passes all 5 quality criteria (DRY, Readable, Maintainable, Performant, Testable) 4. **Documentation**: Code is properly documented with examples 5. **Consistency**: Code style matches existing patterns EXACTLY #### FAILURE CONDITIONS (NEVER ALLOWED!) - ❌ Static text in UI (instant failure) - ❌ Code duplication (instant failure) - ❌ Breaking existing patterns (instant failure) - ❌ Missing tests (instant failure) - ❌ Architecture violations (instant failure) ### 🎯 REMEMBER: When in doubt, ALWAYS follow existing patterns! **These rules apply to ALL development work - NO EXCEPTIONS!** --- This guide ensures consistent, high-quality Flutter development that meets professional standards and security requirements.