Flutter Enterprise - Feature-Based Clean Architecture Lightweight Flutter development skill for building enterprise applications using feature-based clean architecture patterns. Core Philosophy "Feature-first, testable, maintainable enterprise code" - Focus on: | Priority | Area | Purpose | |----------|------|---------| | 1 | Feature-Based Structure | Modular, scalable code organization | | 2 | Clean Architecture | Separation of concerns and testability | | 3 | Dependency Injection | Loose coupling and maintainability | | 4 | Enterprise Patterns | Proven enterprise development practices | | 5…

,\n );\n\n static final RegExp _passwordRegex = RegExp(\n r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}

Flutter Enterprise - Feature-Based Clean Architecture Lightweight Flutter development skill for building enterprise applications using feature-based clean architecture patterns. Core Philosophy "Feature-first, testable, maintainable enterprise code" - Focus on: | Priority | Area | Purpose | |----------|------|---------| | 1 | Feature-Based Structure | Modular, scalable code organization | | 2 | Clean Architecture | Separation of concerns and testability | | 3 | Dependency Injection | Loose coupling and maintainability | | 4 | Enterprise Patterns | Proven enterprise development practices | | 5…

,\n );\n\n static final RegExp _phoneRegex = RegExp(\n r'^\\+?[1-9][0-9]{7,15}

Flutter Enterprise - Feature-Based Clean Architecture Lightweight Flutter development skill for building enterprise applications using feature-based clean architecture patterns. Core Philosophy "Feature-first, testable, maintainable enterprise code" - Focus on: | Priority | Area | Purpose | |----------|------|---------| | 1 | Feature-Based Structure | Modular, scalable code organization | | 2 | Clean Architecture | Separation of concerns and testability | | 3 | Dependency Injection | Loose coupling and maintainability | | 4 | Enterprise Patterns | Proven enterprise development practices | | 5…

,\n );\n\n static ValidationResult validateEmail(String email) {\n if (email.isEmpty) {\n return ValidationResult(false, 'Email is required');\n }\n\n if (!_emailRegex.hasMatch(email)) {\n return ValidationResult(false, 'Invalid email format');\n }\n\n if (email.length > 254) {\n return ValidationResult(false, 'Email too long');\n }\n\n return ValidationResult(true, null);\n }\n\n static ValidationResult validatePassword(String password) {\n if (password.isEmpty) {\n return ValidationResult(false, 'Password is required');\n }\n\n if (password.length \u003c 8) {\n return ValidationResult(false, 'Password must be at least 8 characters');\n }\n\n if (password.length > 128) {\n return ValidationResult(false, 'Password too long');\n }\n\n if (!_passwordRegex.hasMatch(password)) {\n return ValidationResult(false, 'Password must contain uppercase, lowercase, number, and special character');\n }\n\n // Check for common weak passwords\n final weakPasswords = [\n 'password', '123456', 'qwerty', 'admin', 'letmein',\n 'welcome', 'monkey', 'dragon', 'master', 'hello',\n ];\n\n if (weakPasswords.contains(password.toLowerCase())) {\n return ValidationResult(false, 'Password is too common');\n }\n\n return ValidationResult(true, null);\n }\n\n static ValidationResult validatePhone(String phone) {\n if (phone.isEmpty) {\n return ValidationResult(false, 'Phone number is required');\n }\n\n if (!_phoneRegex.hasMatch(phone)) {\n return ValidationResult(false, 'Invalid phone format');\n }\n\n return ValidationResult(true, null);\n }\n\n static ValidationResult validateUsername(String username) {\n if (username.isEmpty) {\n return ValidationResult(false, 'Username is required');\n }\n\n if (username.length \u003c 3) {\n return ValidationResult(false, 'Username must be at least 3 characters');\n }\n\n if (username.length > 50) {\n return ValidationResult(false, 'Username too long');\n }\n\n if (!RegExp(r'^[a-zA-Z0-9_]+

Flutter Enterprise - Feature-Based Clean Architecture Lightweight Flutter development skill for building enterprise applications using feature-based clean architecture patterns. Core Philosophy "Feature-first, testable, maintainable enterprise code" - Focus on: | Priority | Area | Purpose | |----------|------|---------| | 1 | Feature-Based Structure | Modular, scalable code organization | | 2 | Clean Architecture | Separation of concerns and testability | | 3 | Dependency Injection | Loose coupling and maintainability | | 4 | Enterprise Patterns | Proven enterprise development practices | | 5…

).hasMatch(username)) {\n return ValidationResult(false, 'Username can only contain letters, numbers, and underscores');\n }\n\n return ValidationResult(true, null);\n }\n\n static ValidationResult validateRequired(String value, String fieldName) {\n if (value.isEmpty) {\n return ValidationResult(false, '$fieldName is required');\n }\n\n return ValidationResult(true, null);\n }\n}\n\nclass ValidationResult {\n final bool isValid;\n final String? errorMessage;\n\n const ValidationResult(this.isValid, [this.errorMessage]);\n}\n```\n\n## Network Security\n\n### Secure HTTP Client\n\n```dart\n// lib/core/security/secure_http_client.dart\nimport 'package:dio/dio.dart';\nimport 'package:flutter/foundation.dart';\n\nclass SecureHttpClient {\n late Dio _dio;\n\n SecureHttpClient({\n required String baseUrl,\n Duration timeout = const Duration(seconds: 30),\n String? apiKey,\n }) {\n _dio = Dio(BaseOptions(\n baseUrl: baseUrl,\n connectTimeout: timeout,\n receiveTimeout: timeout,\n sendTimeout: timeout,\n headers: _buildSecureHeaders(apiKey),\n ));\n\n _setupInterceptors();\n }\n\n Map\u003cString, String> _buildSecureHeaders(String? apiKey) {\n final headers = \u003cString, String>{\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n 'User-Agent': 'Enterprise-Flutter-App/1.0.0',\n };\n\n if (apiKey != null) {\n headers['Authorization'] = 'Bearer $apiKey';\n }\n\n if (!kReleaseMode) {\n headers['X-Debug-Mode'] = 'true';\n }\n\n return headers;\n }\n\n void _setupInterceptors() {\n _dio.interceptors.add(SecurityInterceptor());\n _dio.interceptors.add(ErrorInterceptor());\n _dio.interceptors.add(LoggingInterceptor());\n }\n\n Future\u003cResponse\u003cT>> get\u003cT>(\n String path, {\n Map\u003cString, dynamic>? queryParameters,\n Options? options,\n }) async {\n try {\n return await _dio.get\u003cT>(\n path,\n queryParameters: queryParameters,\n options: options,\n );\n } on DioException catch (e) {\n throw _handleSecurityException(e);\n }\n }\n\n Future\u003cResponse\u003cT>> post\u003cT>(\n String path, {\n dynamic data,\n Map\u003cString, dynamic>? queryParameters,\n Options? options,\n }) async {\n try {\n return await _dio.post\u003cT>(\n path,\n data: data,\n queryParameters: queryParameters,\n options: options,\n );\n } on DioException catch (e) {\n throw _handleSecurityException(e);\n }\n }\n\n SecurityException _handleSecurityException(DioException exception) {\n switch (exception.type) {\n case DioExceptionType.connectionTimeout:\n case DioExceptionType.sendTimeout:\n case DioExceptionType.receiveTimeout:\n return SecurityException('Request timeout', 'TIMEOUT');\n\n case DioExceptionType.connectionError:\n return SecurityException('Network connection failed', 'CONNECTION_ERROR');\n\n case DioExceptionType.badResponse:\n final statusCode = exception.response?.statusCode;\n\n switch (statusCode) {\n case 401:\n return SecurityException('Unauthorized access', 'UNAUTHORIZED');\n case 403:\n return SecurityException('Access forbidden', 'FORBIDDEN');\n case 429:\n return SecurityException('Too many requests', 'RATE_LIMIT');\n default:\n return SecurityException('Server error: $statusCode', 'SERVER_ERROR');\n }\n\n default:\n return SecurityException('Unknown error: ${exception.message}', 'UNKNOWN_ERROR');\n }\n }\n}\n\nclass SecurityInterceptor extends Interceptor {\n @override\n void onRequest(RequestOptions options, RequestInterceptorHandler handler) {\n // Add security headers\n options.headers['X-Request-ID'] = _generateRequestId();\n options.headers['X-Timestamp'] = DateTime.now().toIso8601String();\n\n handler.next(options);\n }\n\n @override\n void onResponse(Response response, ResponseInterceptorHandler handler) {\n // Log response for security monitoring\n if (!kReleaseMode) {\n debugPrint('Response: ${response.statusCode} - ${response.requestOptions.path}');\n }\n\n handler.next(response);\n }\n\n @override\n void onError(DioException error, ErrorInterceptorHandler handler) {\n // Log errors for security monitoring\n if (!kReleaseMode) {\n debugPrint('Security Error: ${error.message}');\n }\n\n handler.next(error);\n }\n\n String _generateRequestId() {\n return '${DateTime.now().millisecondsSinceEpoch}-${Random().nextInt(10000)}';\n }\n}\n```\n\n### Certificate Pinning\n\n```dart\n// lib/core/security/certificate_pinning.dart\nimport 'package:dio/dio.dart';\nimport 'package:flutter/services.dart';\n\nclass CertificatePinning {\n static const String _expectedSha256 = 'YOUR_EXPECTED_CERTIFICATE_SHA256';\n\n static void setupCertificatePinning(Dio dio) {\n (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {\n client.badCertificateCallback = (cert, host) {\n // Verify certificate\n final certBytes = _getCertificateBytes(cert);\n final certSha256 = _calculateSHA256(certBytes);\n\n if (certSha256 != _expectedSha256) {\n throw SecurityException('Certificate verification failed', 'CERT_PINNING_FAILED');\n }\n\n return true;\n };\n };\n }\n\n static List\u003cint> _getCertificateBytes(X509Certificate cert) {\n // Extract certificate bytes\n return cert.der;\n }\n\n String _calculateSHA256(List\u003cint> bytes) {\n final digest = sha256.convert(bytes);\n return digest.toString();\n }\n}\n```\n\n## Input Validation & Sanitization\n\n### Secure Text Input\n\n```dart\n// lib/core/security/secure_text_input.dart\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\n\nclass SecureTextInput extends StatefulWidget {\n final String? initialValue;\n final String? hintText;\n final bool obscureText;\n final bool enableSuggestions;\n final int? maxLength;\n final List\u003cTextInputFormatter>? inputFormatters;\n final Function(String)? onChanged;\n final Function(String)? onSubmitted;\n\n const SecureTextInput({\n Key? key,\n this.initialValue,\n this.hintText,\n this.obscureText = false,\n this.enableSuggestions = true,\n this.maxLength,\n this.inputFormatters,\n this.onChanged,\n this.onSubmitted,\n }) : super(key: key);\n\n @override\n _SecureTextInputState createState() => _SecureTextInputState();\n}\n\nclass _SecureTextInputState extends State\u003cSecureTextInput> {\n late TextEditingController _controller;\n late FocusNode _focusNode;\n\n @override\n void initState() {\n super.initState();\n _controller = TextEditingController(text: widget.initialValue ?? '');\n _focusNode = FocusNode();\n }\n\n @override\n void dispose() {\n _controller.dispose();\n _focusNode.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return TextField(\n controller: _controller,\n focusNode: _focusNode,\n obscureText: widget.obscureText,\n maxLength: widget.maxLength,\n inputFormatters: [\n FilteringTextInputFormatter.deny(RegExp(r'[\u003c>\"\\']')),\n ...?widget.inputFormatters ?? [],\n ],\n decoration: InputDecoration(\n hintText: widget.hintText,\n suffixIcon: widget.obscureText\n ? IconButton(\n icon: Icon(\n _controller.text.isEmpty ? Icons.visibility_off : Icons.visibility,\n ),\n onPressed: () {\n setState(() {});\n },\n )\n : null,\n ),\n onChanged: (value) {\n final sanitizedValue = _sanitizeInput(value);\n _controller.value = sanitizedValue;\n _controller.selection = TextSelection.fromPosition(\n TextPosition(offset: sanitizedValue.length),\n );\n widget.onChanged?.call(sanitizedValue);\n },\n onSubmitted: (value) {\n final sanitizedValue = _sanitizeInput(value);\n widget.onSubmitted?.call(sanitizedValue);\n },\n );\n }\n\n String _sanitizeInput(String input) {\n // Remove potentially dangerous characters\n return input\n .replaceAll(RegExp(r'[\u003c>\"\\']'), '')\n .replaceAll(RegExp(r'javascript:'), '')\n .replaceAll(RegExp(r'vbscript:'), '')\n .replaceAll(RegExp(r'onload='), '')\n .replaceAll(RegExp(r'onerror='), '');\n }\n}\n```\n\n## Security Best Practices\n\n### 1. Authentication & Authorization\n\n- **Strong password policies**: Enforce complex password requirements\n- **Multi-factor authentication**: Implement 2FA for sensitive operations\n- **Session management**: Use secure session handling with proper timeouts\n- **Role-based access**: Implement principle of least privilege\n- **Account lockout**: Lock accounts after failed login attempts\n- **Secure token storage**: Use encrypted storage for authentication tokens\n\n### 2. Data Protection\n\n- **Encryption at rest**: Encrypt sensitive data stored locally\n- **Encryption in transit**: Use HTTPS/TLS for all communications\n- **Key management**: Securely manage encryption keys\n- **Data minimization**: Collect only necessary data\n- **Data retention**: Implement proper data retention policies\n\n### 3. Network Security\n\n- **Certificate pinning**: Pin SSL certificates to prevent MITM attacks\n- **API security**: Use proper authentication and authorization\n- **Input validation**: Validate all inputs on both client and server\n- **Rate limiting**: Implement rate limiting to prevent abuse\n- **Secure headers**: Use appropriate security headers\n\n### 4. Input Validation & Sanitization\n\n- **Input sanitization**: Remove potentially dangerous characters\n- **Output encoding**: Encode output to prevent XSS attacks\n- **SQL injection prevention**: Use parameterized queries\n- **File upload security**: Validate file types and sizes\n- **Length limits**: Enforce appropriate input length limits\n\n### 5. Mobile Security\n\n- **Root detection**: Detect and handle rooted/jailbroken devices\n- **Screen recording**: Prevent screen recording in sensitive areas\n- **App shielding**: Use app shielding in production\n- **Debug detection**: Disable debug features in production\n- **Backup prevention**: Prevent sensitive data backup\n\n### 6. Compliance & Monitoring\n\n- **Audit logging**: Log all security-relevant events\n- **Compliance checks**: Ensure compliance with regulations\n- **Security testing**: Regular security assessments\n- **Vulnerability scanning**: Regular code and dependency scans\n- **Incident response**: Have a plan for security incidents\n\n## Security Testing\n\n### Security Tests\n\n```dart\n// test/security/security_test.dart\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:flutter_secure_storage/flutter_secure_storage.dart';\n\nvoid main() {\n group('Security Tests', () {\n test('secure storage should encrypt sensitive data', () async {\n final secureStorage = FlutterSecureStorage();\n\n await secureStorage.write(key: 'test_key', value: 'sensitive_data');\n final storedValue = await secureStorage.read(key: 'test_key');\n\n expect(storedValue, isNotNull);\n expect(storedValue, isNot(equals('sensitive_data')));\n });\n\n test('input validation should reject malicious input', () {\n final result = DataValidator.validateEmail('\u003cscript>alert(\"xss\")\u003c/script>');\n\n expect(result.isValid, isFalse);\n expect(result.errorMessage, contains('Invalid email format'));\n });\n\n test('authentication should enforce password complexity', () {\n final weakPassword = 'password123';\n final result = DataValidator.validatePassword(weakPassword);\n\n expect(result.isValid, isFalse);\n expect(result.errorMessage, contains('Password must contain'));\n });\n });\n}\n```\n\n### Penetration Testing\n\n```dart\n// test/security/penetration_test.dart\nimport 'package:flutter_test/flutter_test.dart';\n\nvoid main() {\n group('Penetration Tests', () {\n testWidgets('should prevent XSS attacks', (WidgetTester tester) async {\n await tester.pumpWidget(SecureTextInput());\n\n final textField = tester.widget\u003cSecureTextInput>(find.byType(TextField));\n\n // Attempt XSS injection\n await tester.enterText(find.byType(TextField), '\u003cscript>alert(\"xss\")\u003c/script>');\n\n // Verify script tags are sanitized\n expect(textField.controller.text, isNot(contains('\u003cscript>')));\n expect(textField.controller.text, isNot(contains('javascript:')));\n });\n\n testWidgets('should prevent SQL injection', (WidgetTester tester) async {\n await tester.pumpWidget(SecureTextInput());\n\n final textField = tester.widget\u003cSecureTextInput>(find.byType(TextField));\n\n // Attempt SQL injection\n await tester.enterText(find.byType(TextField), \"'; DROP TABLE users; --\");\n\n // Verify SQL commands are sanitized\n expect(textField.controller.text, isNot(contains('DROP TABLE')));\n expect(textField.controller.text, isNot(contains(';')));\n });\n });\n}\n```\n\nThis comprehensive security guide ensures Flutter enterprise applications implement robust security measures to protect sensitive data and prevent common vulnerabilities.","content_type":"text/markdown; charset=utf-8","language":"markdown","size":25230,"content_sha256":"7476d9ecd9a5902dc10ffa4125d4f6a568c884a03ad54b9bfded3738fc469181"},{"filename":"references/testing-patterns.md","content":"# Testing Patterns for Flutter Enterprise Apps\n\n## Overview\n\nComprehensive testing strategy for Flutter enterprise applications using clean architecture and feature-based structure.\n\n## Testing Pyramid\n\n```\n E2E Tests (10%)\n ─────────────────\n Integration Tests (20%)\n ─────────────────────────\nUnit Tests (70%)\n```\n\n## Unit Testing\n\n### Domain Layer Testing\n\n#### Entity Testing\n```dart\n// test/features/user/domain/entities/user_entity_test.dart\nimport 'package:flutter_test/flutter_test.dart';\nimport '../../../../../lib/features/user/domain/entities/user_entity.dart';\n\nvoid main() {\n group('UserEntity', () {\n const testUser = UserEntity(\n id: '1',\n name: 'Test User',\n email: '[email protected]',\n avatar: 'avatar.jpg',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n );\n\n test('should create UserEntity with valid data', () {\n expect(testUser.id, '1');\n expect(testUser.name, 'Test User');\n expect(testUser.email, '[email protected]');\n expect(testUser.avatar, 'avatar.jpg');\n });\n\n test('should support equality', () {\n const user1 = UserEntity(\n id: '1',\n name: 'Test User',\n email: '[email protected]',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n );\n\n const user2 = UserEntity(\n id: '1',\n name: 'Test User',\n email: '[email protected]',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n );\n\n expect(user1, equals(user2));\n });\n\n test('should copyWith correctly', () {\n final updatedUser = testUser.copyWith(name: 'Updated User');\n\n expect(updatedUser.id, testUser.id);\n expect(updatedUser.name, 'Updated User');\n expect(updatedUser.email, testUser.email);\n });\n\n test('should validate email format', () {\n expect(testUser.isValidEmail(), isTrue);\n\n const invalidUser = UserEntity(\n id: '2',\n name: 'Invalid User',\n email: 'invalid-email',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n );\n\n expect(invalidUser.isValidEmail(), isFalse);\n });\n });\n}\n```\n\n#### Use Case Testing\n```dart\n// test/features/user/domain/usecases/get_users_usecase_test.dart\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:mockito/mockito.dart';\nimport 'package:mockito/annotations.dart';\nimport '../../../../../lib/features/user/domain/entities/user_entity.dart';\nimport '../../../../../lib/features/user/domain/repositories/user_repository.dart';\nimport '../../../../../lib/features/user/domain/usecases/get_users_usecase.dart';\n\nimport 'get_users_usecase_test.mocks.dart';\n\n@GenerateMocks([UserRepository])\nvoid main() {\n late GetUsersUseCase useCase;\n late MockUserRepository mockRepository;\n\n setUp(() {\n mockRepository = MockUserRepository();\n useCase = GetUsersUseCase(mockRepository);\n });\n\n const testUsers = [\n UserEntity(\n id: '1',\n name: 'User 1',\n email: '[email protected]',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n ),\n UserEntity(\n id: '2',\n name: 'User 2',\n email: '[email protected]',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n ),\n ];\n\n test('should get users from repository', () async {\n // Arrange\n when(mockRepository.getUsers(page: 1, limit: 20))\n .thenAnswer((_) async => testUsers);\n\n // Act\n final result = await useCase(page: 1, limit: 20);\n\n // Assert\n expect(result, testUsers);\n verify(mockRepository.getUsers(page: 1, limit: 20));\n });\n\n test('should throw ArgumentError when page is less than 1', () async {\n // Act & Assert\n expect(\n () => useCase(page: 0, limit: 20),\n throwsA(isA\u003cArgumentException>()),\n );\n });\n\n test('should throw ArgumentError when limit is out of range', () async {\n // Act & Assert\n expect(\n () => useCase(page: 1, limit: 0),\n throwsA(isA\u003cArgumentException>()),\n );\n\n expect(\n () => useCase(page: 1, limit: 101),\n throwsA(isA\u003cArgumentException>()),\n );\n });\n\n test('should propagate repository exceptions', () async {\n // Arrange\n when(mockRepository.getUsers(page: 1, limit: 20))\n .thenThrow(ServerException('Network error'));\n\n // Act & Assert\n expect(\n () => useCase(page: 1, limit: 20),\n throwsA(isA\u003cServerException>()),\n );\n });\n}\n```\n\n### Data Layer Testing\n\n#### Model Testing\n```dart\n// test/features/user/data/models/user_model_test.dart\nimport 'package:flutter_test/flutter_test.dart';\nimport '../../../../../lib/features/user/data/models/user_model.dart';\nimport '../../../../../lib/features/user/domain/entities/user_entity.dart';\n\nvoid main() {\n group('UserModel', () {\n const testJson = {\n 'id': '1',\n 'name': 'Test User',\n 'email': '[email protected]',\n 'avatar': 'avatar.jpg',\n 'created_at': '2023-01-01T00:00:00.000Z',\n 'updated_at': '2023-01-01T00:00:00.000Z',\n };\n\n test('should create UserModel from JSON', () {\n final result = UserModel.fromJson(testJson);\n\n expect(result.id, '1');\n expect(result.name, 'Test User');\n expect(result.email, '[email protected]');\n expect(result.avatar, 'avatar.jpg');\n });\n\n test('should convert to JSON', () {\n const model = UserModel(\n id: '1',\n name: 'Test User',\n email: '[email protected]',\n avatar: 'avatar.jpg',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n );\n\n final result = model.toJson();\n\n expect(result, testJson);\n });\n\n test('should convert to entity', () {\n const model = UserModel(\n id: '1',\n name: 'Test User',\n email: '[email protected]',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n );\n\n final entity = model.toEntity();\n\n expect(entity.id, model.id);\n expect(entity.name, model.name);\n expect(entity.email, model.email);\n expect(entity.avatar, model.avatar);\n });\n\n test('should create from entity', () {\n const entity = UserEntity(\n id: '1',\n name: 'Test User',\n email: '[email protected]',\n avatar: 'avatar.jpg',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n );\n\n final model = UserModel.fromEntity(entity);\n\n expect(model.id, entity.id);\n expect(model.name, entity.name);\n expect(model.email, entity.email);\n expect(model.avatar, entity.avatar);\n });\n });\n}\n```\n\n#### Repository Implementation Testing\n```dart\n// test/features/user/data/repositories/user_repository_impl_test.dart\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:mockito/mockito.dart';\nimport 'package:mockito/annotations.dart';\nimport '../../../../../lib/features/user/data/repositories/user_repository_impl.dart';\nimport '../../../../../lib/features/user/data/datasources/user_remote_data_source.dart';\nimport '../../../../../lib/features/user/data/datasources/user_local_data_source.dart';\nimport '../../../../../lib/features/user/domain/entities/user_entity.dart';\nimport '../../../../../lib/core/network/network_info.dart';\nimport '../../../../../lib/core/exceptions/exceptions.dart';\n\nimport 'user_repository_impl_test.mocks.dart';\n\n@GenerateMocks([\n UserRemoteDataSource,\n UserLocalDataSource,\n NetworkInfo,\n])\nvoid main() {\n late UserRepositoryImpl repository;\n late MockUserRemoteDataSource mockRemoteDataSource;\n late MockUserLocalDataSource mockLocalDataSource;\n late MockNetworkInfo mockNetworkInfo;\n\n setUp(() {\n mockRemoteDataSource = MockUserRemoteDataSource();\n mockLocalDataSource = MockUserLocalDataSource();\n mockNetworkInfo = MockNetworkInfo();\n\n repository = UserRepositoryImpl(\n remoteDataSource: mockRemoteDataSource,\n localDataSource: mockLocalDataSource,\n networkInfo: mockNetworkInfo,\n );\n });\n\n group('getUsers', () {\n const testUserModels = [\n UserModel(\n id: '1',\n name: 'User 1',\n email: '[email protected]',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n ),\n ];\n\n test('should return remote data when device is online', () async {\n // Arrange\n when(mockNetworkInfo.isConnected).thenAnswer((_) async => true);\n when(mockRemoteDataSource.getUsers(page: 1, limit: 20))\n .thenAnswer((_) async => testUserModels);\n\n // Act\n final result = await repository.getUsers();\n\n // Assert\n expect(result, isA\u003cList\u003cUserEntity>>());\n verify(mockRemoteDataSource.getUsers(page: 1, limit: 20));\n verify(mockLocalDataSource.cacheUsers(testUserModels));\n });\n\n test('should return cached data when device is offline', () async {\n // Arrange\n when(mockNetworkInfo.isConnected).thenAnswer((_) async => false);\n when(mockLocalDataSource.getCachedUsers())\n .thenAnswer((_) async => testUserModels);\n\n // Act\n final result = await repository.getUsers();\n\n // Assert\n expect(result, isA\u003cList\u003cUserEntity>>());\n verify(mockLocalDataSource.getCachedUsers());\n verifyNever(mockRemoteDataSource.getUsers());\n });\n\n test('should return cached data when server fails', () async {\n // Arrange\n when(mockNetworkInfo.isConnected).thenAnswer((_) async => true);\n when(mockRemoteDataSource.getUsers(page: 1, limit: 20))\n .thenThrow(ServerException('Server error'));\n when(mockLocalDataSource.getCachedUsers())\n .thenAnswer((_) async => testUserModels);\n\n // Act\n final result = await repository.getUsers();\n\n // Assert\n expect(result, isA\u003cList\u003cUserEntity>>());\n verify(mockRemoteDataSource.getUsers(page: 1, limit: 20));\n verify(mockLocalDataSource.getCachedUsers());\n });\n });\n}\n```\n\n### Presentation Layer Testing\n\n#### Provider Testing\n```dart\n// test/features/user/presentation/providers/user_provider_test.dart\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:mockito/mockito.dart';\nimport 'package:mockito/annotations.dart';\nimport '../../../../../lib/features/user/presentation/providers/user_provider.dart';\nimport '../../../../../lib/features/user/domain/entities/user_entity.dart';\nimport '../../../../../lib/features/user/domain/usecases/get_users_usecase.dart';\nimport '../../../../../lib/features/user/domain/usecases/create_user_usecase.dart';\nimport '../../../../../lib/features/user/domain/usecases/search_users_usecase.dart';\n\nimport 'user_provider_test.mocks.dart';\n\n@GenerateMocks([\n GetUsersUseCase,\n CreateUserUseCase,\n SearchUsersUseCase,\n])\nvoid main() {\n late UserProvider provider;\n late MockGetUsersUseCase mockGetUsersUseCase;\n late MockCreateUserUseCase mockCreateUserUseCase;\n late MockSearchUsersUseCase mockSearchUsersUseCase;\n\n setUp(() {\n mockGetUsersUseCase = MockGetUsersUseCase();\n mockCreateUserUseCase = MockCreateUserUseCase();\n mockSearchUsersUseCase = MockSearchUsersUseCase();\n\n provider = UserProvider(\n getUsersUseCase: mockGetUsersUseCase,\n createUserUseCase: mockCreateUserUseCase,\n searchUsersUseCase: mockSearchUsersUseCase,\n );\n });\n\n const testUsers = [\n UserEntity(\n id: '1',\n name: 'User 1',\n email: '[email protected]',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n ),\n ];\n\n test('should initialize with empty state', () {\n expect(provider.users, isEmpty);\n expect(provider.searchResults, isEmpty);\n expect(provider.isLoading, isFalse);\n expect(provider.isSearching, isFalse);\n expect(provider.error, isNull);\n });\n\n test('should load users successfully', () async {\n // Arrange\n when(mockGetUsersUseCase(page: 1, limit: 20))\n .thenAnswer((_) async => testUsers);\n\n // Act\n await provider.loadUsers();\n\n // Assert\n expect(provider.users, testUsers);\n expect(provider.isLoading, isFalse);\n expect(provider.error, isNull);\n verify(mockGetUsersUseCase(page: 1, limit: 20));\n });\n\n test('should handle loading state', () async {\n // Arrange\n when(mockGetUsersUseCase(page: 1, limit: 20))\n .thenAnswer((_) async {\n await Future.delayed(Duration(milliseconds: 100));\n return testUsers;\n });\n\n // Act\n final future = provider.loadUsers();\n\n // Assert loading state\n expect(provider.isLoading, isTrue);\n\n await future;\n\n // Assert completion state\n expect(provider.isLoading, isFalse);\n });\n\n test('should handle error state', () async {\n // Arrange\n when(mockGetUsersUseCase(page: 1, limit: 20))\n .thenThrow(Exception('Network error'));\n\n // Act\n await provider.loadUsers();\n\n // Assert\n expect(provider.users, isEmpty);\n expect(provider.isLoading, isFalse);\n expect(provider.error, 'Exception: Network error');\n });\n\n test('should search users', () async {\n // Arrange\n when(mockSearchUsersUseCase('test'))\n .thenAnswer((_) async => testUsers);\n\n // Act\n await provider.searchUsers('test');\n\n // Assert\n expect(provider.searchResults, testUsers);\n expect(provider.isSearching, isFalse);\n verify(mockSearchUsersUseCase('test'));\n });\n\n test('should clear search results when query is empty', () async {\n // Act\n await provider.searchUsers('');\n\n // Assert\n expect(provider.searchResults, isEmpty);\n expect(provider.isSearching, isFalse);\n verifyNever(mockSearchUsersUseCase(any));\n });\n});\n```\n\n#### Bloc Testing\n```dart\n// test/features/user/presentation/bloc/user_bloc_test.dart\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:bloc_test/bloc_test.dart';\nimport 'package:mockito/mockito.dart';\nimport 'package:mockito/annotations.dart';\nimport '../../../../../lib/features/user/presentation/bloc/user_bloc.dart';\nimport '../../../../../lib/features/user/domain/entities/user_entity.dart';\nimport '../../../../../lib/features/user/domain/usecases/get_users_usecase.dart';\nimport '../../../../../lib/features/user/domain/usecases/create_user_usecase.dart';\nimport '../../../../../lib/features/user/domain/usecases/search_users_usecase.dart';\n\nimport 'user_bloc_test.mocks.dart';\n\n@GenerateMocks([\n GetUsersUseCase,\n CreateUserUseCase,\n SearchUsersUseCase,\n])\nvoid main() {\n late UserBloc bloc;\n late MockGetUsersUseCase mockGetUsersUseCase;\n late MockCreateUserUseCase mockCreateUserUseCase;\n late MockSearchUsersUseCase mockSearchUsersUseCase;\n\n setUp(() {\n mockGetUsersUseCase = MockGetUsersUseCase();\n mockCreateUserUseCase = MockCreateUserUseCase();\n mockSearchUsersUseCase = MockSearchUsersUseCase();\n\n bloc = UserBloc(\n getUsersUseCase: mockGetUsersUseCase,\n createUserUseCase: mockCreateUserUseCase,\n searchUsersUseCase: mockSearchUsersUseCase,\n );\n });\n\n const testUsers = [\n UserEntity(\n id: '1',\n name: 'User 1',\n email: '[email protected]',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n ),\n ];\n\n blocTest\u003cUserBloc, UserState>(\n 'should emit loading and loaded states when loading users',\n build: () {\n when(mockGetUsersUseCase(page: 1, limit: 20))\n .thenAnswer((_) async => testUsers);\n return bloc;\n },\n act: (bloc) => bloc.add(LoadUsers()),\n expect: () => [\n UserLoading(),\n UserLoaded(users: testUsers, hasMore: false),\n ],\n verify: (_) => verify(mockGetUsersUseCase(page: 1, limit: 20)).called(1),\n );\n\n blocTest\u003cUserBloc, UserState>(\n 'should emit error state when loading users fails',\n build: () {\n when(mockGetUsersUseCase(page: 1, limit: 20))\n .thenThrow(Exception('Network error'));\n return bloc;\n },\n act: (bloc) => bloc.add(LoadUsers()),\n expect: () => [\n UserLoading(),\n UserError('Exception: Network error'),\n ],\n verify: (_) => verify(mockGetUsersUseCase(page: 1, limit: 20)).called(1),\n );\n\n blocTest\u003cUserBloc, UserState>(\n 'should emit search results when searching users',\n build: () {\n when(mockSearchUsersUseCase('test'))\n .thenAnswer((_) async => testUsers);\n return bloc;\n },\n act: (bloc) => bloc.add(SearchUsers('test')),\n expect: () => [\n UserLoaded(users: [], hasMore: true, isSearching: true),\n UserLoaded(users: [], hasMore: true, searchResults: testUsers, isSearching: false),\n ],\n verify: (_) => verify(mockSearchUsersUseCase('test')).called(1),\n );\n\n blocTest\u003cUserBloc, UserState>(\n 'should clear search results when query is empty',\n build: () => bloc,\n act: (bloc) => bloc.add(SearchUsers('')),\n expect: () => [\n UserLoaded(users: [], hasMore: true, searchResults: [], isSearching: false),\n ],\n verify: (_) => verifyNever(mockSearchUsersUseCase(any)),\n );\n\n blocTest\u003cUserBloc, UserState>(\n 'should create user and refresh list',\n build: () {\n when(mockCreateUserUseCase(testUsers.first))\n .thenAnswer((_) async => testUsers.first);\n when(mockGetUsersUseCase(page: 1, limit: 20))\n .thenAnswer((_) async => testUsers);\n return bloc;\n },\n act: (bloc) => bloc.add(CreateUser(testUsers.first)),\n expect: () => [\n UserLoading(),\n UserLoaded(users: testUsers, hasMore: false),\n ],\n verify: (_) {\n verify(mockCreateUserUseCase(testUsers.first)).called(1);\n verify(mockGetUsersUseCase(page: 1, limit: 20)).called(1);\n },\n );\n}\n```\n\n#### Riverpod Testing\n```dart\n// test/features/user/presentation/providers/user_provider_test.dart\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:mockito/mockito.dart';\nimport 'package:mockito/annotations.dart';\nimport '../../../../../lib/features/user/presentation/providers/user_provider.dart';\nimport '../../../../../lib/features/user/domain/entities/user_entity.dart';\nimport '../../../../../lib/features/user/domain/usecases/get_users_usecase.dart';\nimport '../../../../../lib/features/user/domain/usecases/create_user_usecase.dart';\nimport '../../../../../lib/features/user/domain/usecases/search_users_usecase.dart';\n\nimport 'user_provider_test.mocks.dart';\n\n@GenerateMocks([\n GetUsersUseCase,\n CreateUserUseCase,\n SearchUsersUseCase,\n])\nvoid main() {\n late UserNotifier notifier;\n late MockGetUsersUseCase mockGetUsersUseCase;\n late MockCreateUserUseCase mockCreateUserUseCase;\n late MockSearchUsersUseCase mockSearchUsersUseCase;\n\n setUp(() {\n mockGetUsersUseCase = MockGetUsersUseCase();\n mockCreateUserUseCase = MockCreateUserUseCase();\n mockSearchUsersUseCase = MockSearchUsersUseCase();\n\n notifier = UserNotifier(\n getUsersUseCase: mockGetUsersUseCase,\n createUserUseCase: mockCreateUserUseCase,\n searchUsersUseCase: mockSearchUsersUseCase,\n );\n });\n\n const testUsers = [\n UserEntity(\n id: '1',\n name: 'User 1',\n email: '[email protected]',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n ),\n ];\n\n test('should initialize with empty state', () {\n expect(notifier.state.users, isEmpty);\n expect(notifier.state.searchResults, isEmpty);\n expect(notifier.state.isLoading, isFalse);\n expect(notifier.state.isSearching, isFalse);\n expect(notifier.state.error, isNull);\n });\n\n test('should load users successfully', () async {\n // Arrange\n when(mockGetUsersUseCase(page: 1, limit: 20))\n .thenAnswer((_) async => testUsers);\n\n // Act\n await notifier.loadUsers();\n\n // Assert\n expect(notifier.state.users, testUsers);\n expect(notifier.state.isLoading, isFalse);\n expect(notifier.state.error, isNull);\n verify(mockGetUsersUseCase(page: 1, limit: 20));\n });\n\n test('should handle loading state', () async {\n // Arrange\n when(mockGetUsersUseCase(page: 1, limit: 20))\n .thenAnswer((_) async {\n await Future.delayed(Duration(milliseconds: 100));\n return testUsers;\n });\n\n // Act\n final future = notifier.loadUsers();\n\n // Assert loading state\n expect(notifier.state.isLoading, isTrue);\n\n await future;\n\n // Assert completion state\n expect(notifier.state.isLoading, isFalse);\n });\n\n test('should handle error state', () async {\n // Arrange\n when(mockGetUsersUseCase(page: 1, limit: 20))\n .thenThrow(Exception('Network error'));\n\n // Act\n await notifier.loadUsers();\n\n // Assert\n expect(notifier.state.users, isEmpty);\n expect(notifier.state.isLoading, isFalse);\n expect(notifier.state.error, 'Exception: Network error');\n });\n\n test('should search users', () async {\n // Arrange\n when(mockSearchUsersUseCase('test'))\n .thenAnswer((_) async => testUsers);\n\n // Act\n await notifier.searchUsers('test');\n\n // Assert\n expect(notifier.state.searchResults, testUsers);\n expect(notifier.state.isSearching, isFalse);\n verify(mockSearchUsersUseCase('test'));\n });\n\n test('should clear search results when query is empty', () async {\n // Act\n await notifier.searchUsers('');\n\n // Assert\n expect(notifier.state.searchResults, isEmpty);\n expect(notifier.state.isSearching, isFalse);\n verifyNever(mockSearchUsersUseCase(any));\n });\n\n test('should clear error', () {\n // Arrange\n notifier.state = notifier.state.copyWith(error: 'Test error');\n\n // Act\n notifier.clearError();\n\n // Assert\n expect(notifier.state.error, isNull);\n });\n\n test('should work with Riverpod provider', () async {\n // Arrange\n final container = ProviderContainer();\n when(mockGetUsersUseCase(page: 1, limit: 20))\n .thenAnswer((_) async => testUsers);\n\n // Override providers for testing\n container.overrideProvider(getUsersUseCaseProvider, (_) => mockGetUsersUseCase);\n container.overrideProvider(createUserUseCaseProvider, (_) => mockCreateUserUseCase);\n container.overrideProvider(searchUsersUseCaseProvider, (_) => mockSearchUsersUseCase);\n\n // Act\n final userNotifier = container.read(userProvider.notifier);\n await userNotifier.loadUsers();\n\n // Assert\n final state = container.read(userProvider);\n expect(state.users, testUsers);\n expect(state.isLoading, isFalse);\n expect(state.error, isNull);\n });\n});\n```\n\n#### GetX Controller Testing\n```dart\n// test/features/user/presentation/controllers/user_controller_test.dart\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:get/get.dart';\nimport 'package:mockito/mockito.dart';\nimport 'package:mockito/annotations.dart';\nimport '../../../../../lib/features/user/presentation/controllers/user_controller.dart';\nimport '../../../../../lib/features/user/domain/entities/user_entity.dart';\nimport '../../../../../lib/features/user/domain/usecases/get_users_usecase.dart';\nimport '../../../../../lib/features/user/domain/usecases/create_user_usecase.dart';\nimport '../../../../../lib/features/user/domain/usecases/search_users_usecase.dart';\n\nimport 'user_controller_test.mocks.dart';\n\n@GenerateMocks([\n GetUsersUseCase,\n CreateUserUseCase,\n SearchUsersUseCase,\n])\nvoid main() {\n late UserController controller;\n late MockGetUsersUseCase mockGetUsersUseCase;\n late MockCreateUserUseCase mockCreateUserUseCase;\n late MockSearchUsersUseCase mockSearchUsersUseCase;\n\n setUp(() {\n // Initialize GetX testing\n Get.testMode = true;\n\n mockGetUsersUseCase = MockGetUsersUseCase();\n mockCreateUserUseCase = MockCreateUserUseCase();\n mockSearchUsersUseCase = MockSearchUsersUseCase();\n\n controller = UserController(\n getUsersUseCase: mockGetUsersUseCase,\n createUserUseCase: mockCreateUserUseCase,\n searchUsersUseCase: mockSearchUsersUseCase,\n );\n });\n\n tearDown(() {\n Get.reset();\n });\n\n const testUsers = [\n UserEntity(\n id: '1',\n name: 'User 1',\n email: '[email protected]',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n ),\n ];\n\n test('should initialize with empty state', () {\n expect(controller.users, isEmpty);\n expect(controller.searchResults, isEmpty);\n expect(controller.isLoading, isFalse);\n expect(controller.isSearching, isFalse);\n expect(controller.error, isNull);\n });\n\n test('should load users successfully', () async {\n // Arrange\n when(mockGetUsersUseCase(page: 1, limit: 20))\n .thenAnswer((_) async => testUsers);\n\n // Act\n await controller.loadUsers();\n\n // Assert\n expect(controller.users, testUsers);\n expect(controller.isLoading, isFalse);\n expect(controller.error, isNull);\n verify(mockGetUsersUseCase(page: 1, limit: 20));\n });\n\n test('should handle loading state', () async {\n // Arrange\n when(mockGetUsersUseCase(page: 1, limit: 20))\n .thenAnswer((_) async {\n await Future.delayed(Duration(milliseconds: 100));\n return testUsers;\n });\n\n // Act\n final future = controller.loadUsers();\n\n // Assert loading state\n expect(controller.isLoading, isTrue);\n\n await future;\n\n // Assert completion state\n expect(controller.isLoading, isFalse);\n });\n\n test('should handle error state', () async {\n // Arrange\n when(mockGetUsersUseCase(page: 1, limit: 20))\n .thenThrow(Exception('Network error'));\n\n // Act\n await controller.loadUsers();\n\n // Assert\n expect(controller.users, isEmpty);\n expect(controller.isLoading, isFalse);\n expect(controller.error, 'Exception: Network error');\n });\n\n test('should search users', () async {\n // Arrange\n when(mockSearchUsersUseCase('test'))\n .thenAnswer((_) async => testUsers);\n\n // Act\n await controller.searchUsers('test');\n\n // Assert\n expect(controller.searchResults, testUsers);\n expect(controller.isSearching, isFalse);\n verify(mockSearchUsersUseCase('test'));\n });\n\n test('should clear search results when query is empty', () async {\n // Act\n await controller.searchUsers('');\n\n // Assert\n expect(controller.searchResults, isEmpty);\n expect(controller.isSearching, isFalse);\n verifyNever(mockSearchUsersUseCase(any));\n });\n\n test('should clear error', () {\n // Arrange\n controller._error.value = 'Test error';\n\n // Act\n controller.clearError();\n\n // Assert\n expect(controller.error, isNull);\n });\n\n test('should work with GetX binding', () async {\n // Arrange\n final binding = UserBinding();\n when(mockGetUsersUseCase(page: 1, limit: 20))\n .thenAnswer((_) async => testUsers);\n\n // Act\n binding.dependencies();\n final userController = Get.find\u003cUserController>();\n await userController.loadUsers();\n\n // Assert\n expect(userController.users, testUsers);\n expect(userController.isLoading, isFalse);\n expect(userController.error, isNull);\n });\n}\n```\n\n## Widget Testing\n\n### Page Testing\n```dart\n// test/features/user/presentation/pages/user_list_page_test.dart\nimport 'package:flutter/material.dart';\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:mockito/mockito.dart';\nimport 'package:provider/provider.dart';\nimport '../../../../../lib/features/user/presentation/pages/user_list_page.dart';\nimport '../../../../../lib/features/user/presentation/providers/user_provider.dart';\nimport '../../../../../lib/features/user/domain/entities/user_entity.dart';\n\nimport '../../mocks/user_provider_mock.dart';\n\nvoid main() {\n group('UserListPage', () {\n late MockUserProvider mockProvider;\n\n setUp(() {\n mockProvider = MockUserProvider();\n });\n\n testWidgets('should show loading indicator', (WidgetTester tester) async {\n // Arrange\n when(mockProvider.isLoading).thenReturn(true);\n when(mockProvider.users).thenReturn([]);\n when(mockProvider.error).thenReturn(null);\n\n // Act\n await tester.pumpWidget(\n ChangeNotifierProvider\u003cUserProvider>.value(\n value: mockProvider,\n child: MaterialApp(home: UserListPage()),\n ),\n );\n\n // Assert\n expect(find.byType(CircularProgressIndicator), findsOneWidget);\n });\n\n testWidgets('should display user list', (WidgetTester tester) async {\n // Arrange\n const testUsers = [\n UserEntity(\n id: '1',\n name: 'User 1',\n email: '[email protected]',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n ),\n ];\n\n when(mockProvider.isLoading).thenReturn(false);\n when(mockProvider.users).thenReturn(testUsers);\n when(mockProvider.error).thenReturn(null);\n\n // Act\n await tester.pumpWidget(\n ChangeNotifierProvider\u003cUserProvider>.value(\n value: mockProvider,\n child: MaterialApp(home: UserListPage()),\n ),\n );\n\n // Assert\n expect(find.text('User 1'), findsOneWidget);\n expect(find.text('[email protected]'), findsOneWidget);\n });\n\n testWidgets('should show error message', (WidgetTester tester) async {\n // Arrange\n when(mockProvider.isLoading).thenReturn(false);\n when(mockProvider.users).thenReturn([]);\n when(mockProvider.error).thenReturn('Network error');\n\n // Act\n await tester.pumpWidget(\n ChangeNotifierProvider\u003cUserProvider>.value(\n value: mockProvider,\n child: MaterialApp(home: UserListPage()),\n ),\n );\n\n // Assert\n expect(find.text('Error: Network error'), findsOneWidget);\n });\n });\n}\n```\n\n#### GetX Widget Testing\n```dart\n// test/features/user/presentation/pages/user_list_page_test.dart\nimport 'package:flutter/material.dart';\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:get/get.dart';\nimport 'package:get_test/get_test.dart';\nimport '../../../../../lib/features/user/presentation/pages/user_list_page.dart';\nimport '../../../../../lib/features/user/presentation/controllers/user_controller.dart';\nimport '../../../../../lib/features/user/domain/entities/user_entity.dart';\n\nimport '../../mocks/user_controller_mock.dart';\n\nvoid main() {\n group('UserListPage with GetX', () {\n setUp(() {\n Get.testMode = true;\n });\n\n tearDown(() {\n Get.reset();\n });\n\n testWidgets('should show loading indicator', (WidgetTester tester) async {\n // Arrange\n final controller = MockUserController();\n when(controller.isLoading).thenReturn(true);\n when(controller.users).thenReturn([]);\n when(controller.error).thenReturn(null);\n\n // Act\n await tester.pumpWidget(\n GetMaterialApp(\n home: UserListPage(),\n bindings: BindingsBuilder(() {\n Get.lazyPut\u003cUserController>(() => controller);\n }),\n ),\n );\n\n // Assert\n expect(find.byType(CircularProgressIndicator), findsOneWidget);\n });\n\n testWidgets('should display user list', (WidgetTester tester) async {\n // Arrange\n const testUsers = [\n UserEntity(\n id: '1',\n name: 'User 1',\n email: '[email protected]',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n ),\n ];\n\n final controller = MockUserController();\n when(controller.isLoading).thenReturn(false);\n when(controller.users).thenReturn(testUsers);\n when(controller.error).thenReturn(null);\n\n // Act\n await tester.pumpWidget(\n GetMaterialApp(\n home: UserListPage(),\n bindings: BindingsBuilder(() {\n Get.lazyPut\u003cUserController>(() => controller);\n }),\n ),\n );\n\n // Assert\n expect(find.text('User 1'), findsOneWidget);\n expect(find.text('[email protected]'), findsOneWidget);\n });\n\n testWidgets('should show error message', (WidgetTester tester) async {\n // Arrange\n final controller = MockUserController();\n when(controller.isLoading).thenReturn(false);\n when(controller.users).thenReturn([]);\n when(controller.error).thenReturn('Network error');\n\n // Act\n await tester.pumpWidget(\n GetMaterialApp(\n home: UserListPage(),\n bindings: BindingsBuilder(() {\n Get.lazyPut\u003cUserController>(() => controller);\n }),\n ),\n );\n\n // Assert\n expect(find.text('Error: Network error'), findsOneWidget);\n });\n });\n}\n```\n\n## Integration Testing\n\n### Feature Integration Testing\n```dart\n// integration_test/user_feature_integration_test.dart\nimport 'package:flutter/material.dart';\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:integration_test/integration_test.dart';\nimport 'package:my_app/main.dart' as app;\n\nvoid main() {\n IntegrationTestWidgetsFlutterBinding.ensureInitialized();\n\n group('User Feature Integration Tests', () {\n testWidgets('should create and display user', (WidgetTester tester) async {\n // Arrange\n app.main();\n await tester.pumpAndSettle();\n\n // Navigate to user creation\n await tester.tap(find.byIcon(Icons.add));\n await tester.pumpAndSettle();\n\n // Fill user form\n await tester.enterText(find.byKey(Key('name_field')), 'Test User');\n await tester.enterText(find.byKey(Key('email_field')), '[email protected]');\n\n // Submit form\n await tester.tap(find.byKey(Key('submit_button')));\n await tester.pumpAndSettle();\n\n // Assert user is created and displayed\n expect(find.text('Test User'), findsOneWidget);\n expect(find.text('[email protected]'), findsOneWidget);\n });\n\n testWidgets('should search users', (WidgetTester tester) async {\n // Arrange\n app.main();\n await tester.pumpAndSettle();\n\n // Search for user\n await tester.enterText(find.byType(TextField), 'Test');\n await tester.pumpAndSettle();\n\n // Assert search results\n expect(find.text('Test User'), findsOneWidget);\n });\n });\n}\n```\n\n## Testing Utilities\n\n### Test Helpers\n```dart\n// test/helpers/test_helpers.dart\nimport 'package:flutter/material.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nclass TestHelpers {\n static Widget createWidgetUnderTest(Widget child) {\n return MaterialApp(\n home: Scaffold(body: child),\n );\n }\n\n static Future\u003cvoid> pumpAndSettleWithDelay(\n WidgetTester tester, {\n Duration duration = const Duration(milliseconds: 100),\n }) async {\n await tester.pump();\n await Future.delayed(duration);\n await tester.pumpAndSettle();\n }\n\n static void expectNoOverflow() {\n expect(find.byType(Overflow), findsNothing);\n }\n\n static void expectVisible(Key key) {\n expect(find.byKey(key), findsOneWidget);\n }\n\n static void expectHidden(Key key) {\n expect(find.byKey(key), findsNothing);\n }\n}\n```\n\n### Mock Factories\n```dart\n// test/mocks/mock_factories.dart\nimport '../../../lib/features/user/domain/entities/user_entity.dart';\nimport '../../../lib/features/user/data/models/user_model.dart';\n\nclass MockFactories {\n static UserEntity createTestUser({\n String id = '1',\n String name = 'Test User',\n String email = '[email protected]',\n }) {\n return const UserEntity(\n id: '1',\n name: 'Test User',\n email: '[email protected]',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n );\n }\n\n static List\u003cUserEntity> createTestUserList({int count = 3}) {\n return List.generate(count, (index) => createTestUser(\n id: index.toString(),\n name: 'User $index',\n email: '[email protected]',\n ));\n }\n\n static UserModel createTestUserModel({\n String id = '1',\n String name = 'Test User',\n String email = '[email protected]',\n }) {\n return const UserModel(\n id: '1',\n name: 'Test User',\n email: '[email protected]',\n createdAt: '2023-01-01T00:00:00.000Z',\n updatedAt: '2023-01-01T00:00:00.000Z',\n );\n }\n}\n```\n\n## Test Configuration\n\n### pubspec.yaml Test Dependencies\n```yaml\ndev_dependencies:\n flutter_test:\n sdk: flutter\n integration_test:\n sdk: flutter\n mockito: ^5.4.2\n build_runner: ^2.4.6\n mockito_generator: ^5.4.2\n fake_async: ^1.3.1\n network_image_mock: ^2.1.1\n golden_toolkit: ^0.15.0\n get_test: ^3.0.0 # For GetX testing\n```\n\n### Test Runner Configuration\n```dart\n// test/test_config.dart\nimport 'package:flutter_test/flutter_test.dart';\n\nvoid main() {\n // Global test setup\n setUpAll(() {\n // Initialize test dependencies\n });\n\n tearDownAll(() {\n // Clean up test dependencies\n });\n}\n```\n\n## Best Practices\n\n1. **Test Pyramid**: Focus on unit tests (70%), integration tests (20%), and E2E tests (10%)\n2. **Descriptive Tests**: Use clear, descriptive test names that explain what is being tested\n3. **AAA Pattern**: Arrange, Act, Assert structure for tests\n4. **Mock External Dependencies**: Mock all external dependencies in unit tests\n5. **Test Coverage**: Aim for 80%+ coverage on domain and data layers\n6. **Independent Tests**: Tests should not depend on each other\n7. **Fast Tests**: Unit tests should run in milliseconds\n8. **CI Integration**: Run tests automatically on every commit","content_type":"text/markdown; charset=utf-8","language":"markdown","size":37348,"content_sha256":"4e26d3b873c02a1657ea3acb733e6a55f52e0d30606e68c3c0ef842bbb049b95"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Flutter Enterprise - Feature-Based Clean Architecture","type":"text"}]},{"type":"paragraph","content":[{"text":"Lightweight Flutter development skill for building enterprise applications using feature-based clean architecture patterns.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Philosophy","type":"text"}]},{"type":"paragraph","content":[{"text":"\"Feature-first, testable, maintainable enterprise code\"","type":"text","marks":[{"type":"strong"}]},{"text":" - Focus on:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Priority","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Area","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Purpose","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Feature-Based Structure","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Modular, scalable code organization","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"2","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Clean Architecture","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Separation of concerns and testability","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"3","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Dependency Injection","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Loose coupling and maintainability","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"4","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Enterprise Patterns","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Proven enterprise development practices","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"5","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Code Generation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Boilerplate reduction and consistency","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Development Workflow","type":"text"}]},{"type":"paragraph","content":[{"text":"Execute phases sequentially. Complete each before proceeding.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 1: Analyze Requirements","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Feature identification","type":"text","marks":[{"type":"strong"}]},{"text":" - Identify distinct business features","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Data flow analysis","type":"text","marks":[{"type":"strong"}]},{"text":" - Map data dependencies between features","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Integration points","type":"text","marks":[{"type":"strong"}]},{"text":" - Define external service integrations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Scalability requirements","type":"text","marks":[{"type":"strong"}]},{"text":" - Plan for future feature additions","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Output: Feature breakdown with dependency mapping.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 2: Design Feature Architecture","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Feature boundary definition","type":"text","marks":[{"type":"strong"}]},{"text":" - Define clear feature boundaries","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Data layer planning","type":"text","marks":[{"type":"strong"}]},{"text":" - Design repositories and data sources","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Domain modeling","type":"text","marks":[{"type":"strong"}]},{"text":" - Create entities and use cases","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Presentation layer design","type":"text","marks":[{"type":"strong"}]},{"text":" - Plan UI components and state management","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Output: Feature architecture diagram and data contracts.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 3: Implement Core Structure","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Project setup","type":"text","marks":[{"type":"strong"}]},{"text":" - Create feature-based directory structure","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dependency injection","type":"text","marks":[{"type":"strong"}]},{"text":" - Set up service locator or DI container","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Core utilities","type":"text","marks":[{"type":"strong"}]},{"text":" - Create shared utilities and constants","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Navigation setup","type":"text","marks":[{"type":"strong"}]},{"text":" - Implement routing structure","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Feature Structure Pattern:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"lib/\n├── core/\n│ ├── constants/\n│ ├── errors/\n│ ├── network/\n│ ├── utils/\n│ └── widgets/\n├── features/\n│ ├── feature_name/\n│ │ ├── data/\n│ │ │ ├── datasources/\n│ │ │ ├── models/\n│ │ │ └── repositories/\n│ │ ├── domain/\n│ │ │ ├── entities/\n│ │ │ ├── repositories/\n│ │ │ └── usecases/\n│ │ └── presentation/\n│ │ ├── pages/\n│ │ ├── widgets/\n│ │ └── providers/\n│ └── ...\n└── main.dart","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 4: Implement Feature Modules","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Data layer","type":"text","marks":[{"type":"strong"}]},{"text":" - Implement repositories and data sources","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Domain layer","type":"text","marks":[{"type":"strong"}]},{"text":" - Create business logic and use cases","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Presentation layer","type":"text","marks":[{"type":"strong"}]},{"text":" - Build UI components and state management","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Feature integration","type":"text","marks":[{"type":"strong"}]},{"text":" - Connect feature to main app","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Clean Architecture Implementation:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"dart"},"content":[{"text":"// Domain Layer - Entity\nclass User {\n final String id;\n final String name;\n final String email;\n\n User({required this.id, required this.name, required this.email});\n}\n\n// Domain Layer - Repository (Abstract)\nabstract class UserRepository {\n Future\u003cList\u003cUser>> getUsers();\n Future\u003cUser> getUserById(String id);\n}\n\n// Domain Layer - Use Case\nclass GetUsersUseCase {\n final UserRepository repository;\n\n GetUsersUseCase(this.repository);\n\n Future\u003cList\u003cUser>> call() async {\n return await repository.getUsers();\n }\n}\n\n// Data Layer - Repository Implementation\nclass UserRepositoryImpl implements UserRepository {\n final RemoteDataSource remoteDataSource;\n\n UserRepositoryImpl(this.remoteDataSource);\n\n @override\n Future\u003cList\u003cUser>> getUsers() async {\n final userModels = await remoteDataSource.getUsers();\n return userModels.map((model) => model.toEntity()).toList();\n }\n}","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 5: Setup Testing Structure","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Unit tests","type":"text","marks":[{"type":"strong"}]},{"text":" - Test domain layer and use cases","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Integration tests","type":"text","marks":[{"type":"strong"}]},{"text":" - Test data layer and repositories","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Widget tests","type":"text","marks":[{"type":"strong"}]},{"text":" - Test presentation layer components","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Test utilities","type":"text","marks":[{"type":"strong"}]},{"text":" - Create mock objects and test helpers","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Reference","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Feature-Based Architecture Patterns","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Layer","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Responsibility","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Key Components","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Presentation","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"UI and State Management","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Pages, Widgets, Providers/Bloc","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Domain","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Business Logic","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Entities, Use Cases, Repository Interfaces","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Data","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Data Implementation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Models, Data Sources, Repository Implementations","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Dependency Injection Setup","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"dart"},"content":[{"text":"// main.dart\nvoid main() {\n // Initialize dependencies\n final serviceLocator = GetIt.instance;\n\n // Data sources\n serviceLocator.registerLazySingleton\u003cRemoteDataSource>(\n () => RemoteDataSourceImpl(httpClient: serviceLocator()));\n\n // Repositories\n serviceLocator.registerLazySingleton\u003cUserRepository>(\n () => UserRepositoryImpl(serviceLocator()));\n\n // Use cases\n serviceLocator.registerFactory\u003cGetUsersUseCase>(\n () => GetUsersUseCase(serviceLocator()));\n\n runApp(MyApp());\n}","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"State Management Patterns","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill now supports ","type":"text"},{"text":"state management neutrality","type":"text","marks":[{"type":"strong"}]},{"text":" with equivalent implementations for all four major approaches:","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Provider Pattern","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"dart"},"content":[{"text":"// Presentation Layer - Provider\nclass UserProvider extends ChangeNotifier {\n final GetUsersUseCase getUsersUseCase;\n\n List\u003cUser> _users = [];\n bool _isLoading = false;\n\n UserProvider({required this.getUsersUseCase});\n\n List\u003cUser> get users => _users;\n bool get isLoading => _isLoading;\n\n Future\u003cvoid> loadUsers() async {\n _isLoading = true;\n notifyListeners();\n\n try {\n _users = await getUsersUseCase();\n } catch (e) {\n // Handle error\n } finally {\n _isLoading = false;\n notifyListeners();\n }\n }\n}","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Bloc Pattern","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"dart"},"content":[{"text":"// Presentation Layer - Bloc\nabstract class UserEvent extends Equatable {}\nclass LoadUsers extends UserEvent {}\n\nabstract class UserState extends Equatable {}\nclass UserLoading extends UserState {}\nclass UserLoaded extends UserState {\n final List\u003cUser> users;\n UserLoaded(this.users);\n @override\n List\u003cObject> get props => [users];\n}\n\nclass UserBloc extends Bloc\u003cUserEvent, UserState> {\n final GetUsersUseCase getUsersUseCase;\n\n UserBloc({required this.getUsersUseCase}) : super(UserInitial()) {\n on\u003cLoadUsers>(_onLoadUsers);\n }\n\n Future\u003cvoid> _onLoadUsers(LoadUsers event, Emitter\u003cUserState> emit) async {\n emit(UserLoading());\n try {\n final users = await getUsersUseCase();\n emit(UserLoaded(users));\n } catch (e) {\n // Handle error\n }\n }\n}","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Riverpod Pattern","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"dart"},"content":[{"text":"// Presentation Layer - Riverpod\nclass UserNotifier extends StateNotifier\u003cAsyncValue\u003cList\u003cUser>>> {\n final GetUsersUseCase getUsersUseCase;\n\n UserNotifier({required this.getUsersUseCase}) : super(const AsyncValue.loading());\n\n Future\u003cvoid> loadUsers() async {\n state = const AsyncValue.loading();\n try {\n final users = await getUsersUseCase();\n state = AsyncValue.data(users);\n } catch (e, stackTrace) {\n state = AsyncValue.error(e, stackTrace);\n }\n }\n}\n\nfinal userProvider = StateNotifierProvider\u003cUserNotifier, AsyncValue\u003cList\u003cUser>>>((ref) {\n return UserNotifier(getUsersUseCase: ref.watch(getUsersUseCaseProvider));\n});","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"GetX Pattern","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"dart"},"content":[{"text":"// Presentation Layer - GetX\nclass UserController extends GetxController {\n final GetUsersUseCase getUsersUseCase;\n\n UserController({required this.getUsersUseCase});\n\n final RxList\u003cUser> _users = \u003cUser>[].obs;\n final RxBool _isLoading = false.obs;\n\n List\u003cUser> get users => _users;\n bool get isLoading => _isLoading.value;\n\n Future\u003cvoid> loadUsers() async {\n _isLoading.value = true;\n try {\n final userList = await getUsersUseCase();\n _users.assignAll(userList);\n } catch (e) {\n // Handle error\n } finally {\n _isLoading.value = false;\n }\n }\n}\n\n// Page Example with GetX\nclass UserListPage extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return GetBuilder\u003cUserController>(\n init: UserController(getUsersUseCase: Get.find()),\n builder: (controller) {\n return Scaffold(\n appBar: AppBar(title: Text('Users')),\n body: Obx(() {\n if (controller.isLoading.value) {\n return Center(child: CircularProgressIndicator());\n }\n\n return ListView.builder(\n itemCount: controller.users.length,\n itemBuilder: (context, index) {\n final user = controller.users[index];\n return UserTile(user: user);\n },\n );\n }),\n );\n },\n );\n }\n}","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Resources","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Architecture patterns","type":"text","marks":[{"type":"strong"}]},{"text":": See ","type":"text"},{"text":"references/clean-architecture.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Feature templates","type":"text","marks":[{"type":"strong"}]},{"text":": See ","type":"text"},{"text":"references/feature-templates.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Testing patterns","type":"text","marks":[{"type":"strong"}]},{"text":": See ","type":"text"},{"text":"references/testing-patterns.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Code generation","type":"text","marks":[{"type":"strong"}]},{"text":": See ","type":"text"},{"text":"references/code-generation.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Technical Stack","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Architecture","type":"text","marks":[{"type":"strong"}]},{"text":": Clean Architecture with feature-based structure","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"State Management","type":"text","marks":[{"type":"strong"}]},{"text":": Provider/Bloc/Riverpod/GetX (state management neutral - all four approaches fully supported with equivalent examples)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dependency Injection","type":"text","marks":[{"type":"strong"}]},{"text":": GetIt/Injectable","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Code Generation","type":"text","marks":[{"type":"strong"}]},{"text":": build_runner, json_annotation, freezed","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Testing","type":"text","marks":[{"type":"strong"}]},{"text":": mockito, bloc_test, widget testing","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Best Practices","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Feature Independence","type":"text","marks":[{"type":"strong"}]},{"text":": Each feature should be self-contained","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dependency Rule","type":"text","marks":[{"type":"strong"}]},{"text":": Dependencies point inward (Presentation → Domain ← Data)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Interface Segregation","type":"text","marks":[{"type":"strong"}]},{"text":": Keep interfaces small and focused","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Single Responsibility","type":"text","marks":[{"type":"strong"}]},{"text":": Each class has one reason to change","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Test Coverage","type":"text","marks":[{"type":"strong"}]},{"text":": Aim for 80%+ coverage on domain and data layers","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"This Flutter enterprise skill transforms complex enterprise app development into a systematic process that ensures maintainable, scalable, and testable applications using feature-based clean architecture.","type":"text"}]}]},"metadata":{"date":"2026-06-05","name":"flutter-enterprise","author":"@skillopedia","source":{"stars":3,"repo_name":"skills-collection","origin_url":"https://github.com/ajianaz/skills-collection/blob/HEAD/skills/flutter-enterprise/SKILL.md","repo_owner":"ajianaz","body_sha256":"48fc5ab5c9fdd3926146d022344c38892e02a3ba6c9809543d652ea312532373","cluster_key":"061c7753258132e835dc9e2dcb973864d462fcb30fb615dd0ad49b5377c7b1e0","clean_bundle":{"format":"clean-skill-bundle-v1","source":"ajianaz/skills-collection/skills/flutter-enterprise/SKILL.md","attachments":[{"id":"e59b337f-8c15-5d0d-acdd-ecf5c92ad991","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e59b337f-8c15-5d0d-acdd-ecf5c92ad991/attachment.md","path":"assets/project-structure.md","size":24951,"sha256":"d121e48a59554822866182e8751cf492a8250aec1332893b5ee54c9007093bde","contentType":"text/markdown; charset=utf-8"},{"id":"3c400565-e05f-5ebc-8a3e-299692e507c1","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3c400565-e05f-5ebc-8a3e-299692e507c1/attachment.md","path":"references/clean-architecture.md","size":17711,"sha256":"d95bb1789f8c51c1eba617c02e27859a93195b4f1704cf25393dca94c6c35dd4","contentType":"text/markdown; charset=utf-8"},{"id":"7d4b921d-e40d-546f-9487-a411d3f5c378","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7d4b921d-e40d-546f-9487-a411d3f5c378/attachment.md","path":"references/code-generation.md","size":14128,"sha256":"3946621b5251b2a811868bcaa551680cf888b63fcdd1800841279dd5548a2585","contentType":"text/markdown; charset=utf-8"},{"id":"2ddbf36a-1977-54cf-af69-efbe262ca16a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2ddbf36a-1977-54cf-af69-efbe262ca16a/attachment.md","path":"references/error-handling.md","size":22998,"sha256":"38910099e17f7f52a94619300f4e3f25f0b54d0a2a639b47ee0da080a3e96258","contentType":"text/markdown; charset=utf-8"},{"id":"88407557-52fe-5765-8810-d7d25718ec73","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/88407557-52fe-5765-8810-d7d25718ec73/attachment.md","path":"references/feature-templates.md","size":35223,"sha256":"ad78cf8154262953276411f8ab283d5b39b674bd20e009fe4c2a1217011129d6","contentType":"text/markdown; charset=utf-8"},{"id":"f7eca471-c9da-5db0-bd3b-baaec128a606","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f7eca471-c9da-5db0-bd3b-baaec128a606/attachment.md","path":"references/performance-optimization.md","size":21495,"sha256":"3491698655dcb585bd7489f53c97ba58b089e628ce8945d55ab81f512d97b33f","contentType":"text/markdown; charset=utf-8"},{"id":"639d7bae-061f-5a87-b861-147f0d15e517","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/639d7bae-061f-5a87-b861-147f0d15e517/attachment.md","path":"references/security-best-practices.md","size":25230,"sha256":"7476d9ecd9a5902dc10ffa4125d4f6a568c884a03ad54b9bfded3738fc469181","contentType":"text/markdown; charset=utf-8"},{"id":"b3403b89-7994-5015-aada-2452db15c405","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b3403b89-7994-5015-aada-2452db15c405/attachment.md","path":"references/testing-patterns.md","size":37348,"sha256":"4e26d3b873c02a1657ea3acb733e6a55f52e0d30606e68c3c0ef842bbb049b95","contentType":"text/markdown; charset=utf-8"}],"bundle_sha256":"5ec7ec801709568def29a18e4504a243b8689314aefeb0dd1ca0c5fdeeb49552","attachment_count":8,"text_attachments":8,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/flutter-enterprise/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"web-development","category_label":"Web"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"web-development","import_tag":"clean-skills-v1","description":"Lightweight Flutter enterprise development skill focused on feature-based clean architecture. Use when user asks to: (1) Build enterprise Flutter apps with clean architecture, (2) Implement feature-based modular structure, (3) Set up scalable Flutter project organization, (4) Create maintainable enterprise codebase. Triggers: Flutter enterprise app, clean architecture Flutter, feature-based Flutter, enterprise Flutter structure, modular Flutter architecture"}},"renderedAt":1782980013895}

Flutter Enterprise - Feature-Based Clean Architecture Lightweight Flutter development skill for building enterprise applications using feature-based clean architecture patterns. Core Philosophy "Feature-first, testable, maintainable enterprise code" - Focus on: | Priority | Area | Purpose | |----------|------|---------| | 1 | Feature-Based Structure | Modular, scalable code organization | | 2 | Clean Architecture | Separation of concerns and testability | | 3 | Dependency Injection | Loose coupling and maintainability | | 4 | Enterprise Patterns | Proven enterprise development practices | | 5…