Un proyecto Flutter estructurado con Clean Architecture para facilitar el desarrollo escalable y mantenible.
- Arquitectura del Proyecto
- Estructura Detallada
- Capas de Clean Architecture
- Features Implementados
- Guía de Desarrollo
- Instalación
- Convenciones de Código
Este proyecto sigue los principios de Clean Architecture organizando el código en capas claramente definidas con separación de responsabilidades. La arquitectura está diseñada para ser escalable, mantenible y fácil de testear.
lib/
├── core/ # Funcionalidad compartida en toda la aplicación
│ ├── api/ # Servicios base para comunicación con APIs
│ │ ├── base_api_service.dart # Servicio base con Dio
│ │ └── api_client.dart # Cliente HTTP configurado
│ │
│ ├── config/ # Configuraciones de la aplicación
│ │ ├── app_config.dart # Configuración global de la app
│ │ └── config_reader.dart # Lector de archivos de configuración
│ │
│ ├── constants/ # Constantes globales
│ │ ├── app_constants.dart # Constantes de la aplicación
│ │ └── app_end_point.dart # URLs y endpoints de API
│ │
│ ├── common/ # Recursos comunes compartidos
│ │ ├── domain/
│ │ │ └── entities/ # Entidades compartidas entre features
│ │ │ ├── user_models.dart # Modelo de usuario
│ │ │ ├── business_models.dart # Modelo de negocio
│ │ │ ├── response_api_models.dart # Respuestas API estándar
│ │ │ └── transaction_api_models.dart # Modelos de transacciones
│ │ └── provider/ # Providers globales
│ │ ├── app_state_provider.dart # Estado global de la app
│ │ └── session_provider.dart # Gestión de sesión de usuario
│ │
│ ├── errors/ # Manejo de errores y excepciones
│ ├── extensions/ # Extensiones de Dart/Flutter
│ ├── helpers/ # Funciones auxiliares
│ │
│ ├── router/ # Configuración de rutas/navegación
│ │ └── app_router.dart # Definición de rutas de la app
│ │
│ ├── mock/ # Datos mock para desarrollo
│ │ └── routes_mock.dart # Rutas y datos simulados
│ │
│ ├── theme/ # Temas y estilos
│ │ └── app_theme.dart # Tema personalizado de la app
│ │
│ ├── ui/ # UI compartida
│ │ └── widgets/ # Widgets reutilizables globales
│ │ ├── app_bar_widget.dart # AppBar personalizado
│ │ ├── drawer_widget.dart # Drawer de navegación
│ │ ├── business_card_widget.dart # Tarjeta de negocio
│ │ └── logout_button_widget.dart # Botón de cerrar sesión
│ │
│ └── utils/ # Utilidades generales
│ ├── console.dart # Utilidades de consola/debug
│ ├── alerts.dart # Sistema de alertas
│ └── text_helpers.dart # Helpers para texto
│
├── features/ # Módulos de funcionalidad (por feature)
│ │
│ ├── auth/ # 🔐 Feature de autenticación
│ │ ├── data/ # Capa de datos
│ │ │ ├── sources/ # Fuentes de datos
│ │ │ │ └── auth_api.dart # API de autenticación
│ │ │ ├── models/ # DTOs y modelos
│ │ │ │ ├── login_response_model.dart
│ │ │ │ └── auth_state.dart # Estados de autenticación
│ │ │ └── repositories/ # Implementación de repositorios
│ │ │
│ │ ├── domain/ # Capa de dominio (lógica de negocio)
│ │ │ ├── entities/ # Entidades del negocio
│ │ │ ├── repositories/ # Interfaces de repositorios
│ │ │ └── usecases/ # Casos de uso
│ │ │
│ │ └── presentation/ # Capa de presentación (UI)
│ │ ├── screens/ # Pantallas
│ │ │ ├── login_provider_screen.dart
│ │ │ ├── login_riverpod_screen.dart
│ │ │ └── login_riverpod_new_screen.dart
│ │ ├── provider/ # Gestión de estado
│ │ │ ├── auth_provider.dart # Provider (ChangeNotifier)
│ │ │ ├── auth_riverpod.dart # Riverpod StateNotifier
│ │ │ └── auth_riverpod_new.dart # Riverpod AsyncNotifier
│ │ └── widgets/ # Widgets específicos
│ │ ├── login_background_widget.dart
│ │ └── card_container_widget.dart
│ │
│ ├── home/ # 🏠 Feature de pantalla principal
│ │ ├── data/ # Capa de datos
│ │ ├── presentation/
│ │ ├── screens/ # Múltiples diseños de home
│ │ │ ├── home_dashboard_screen.dart
│ │ │ ├── home_grid_screen.dart
│ │ │ ├── home_categori_screen.dart
│ │ │ ├── home_bento_box_screen.dart
│ │ │ ├── home_radial_screen.dart
│ │ │ ├── selection_business_grid_screen.dart
│ │ │ └── selection_bussiness_screen.dart
│ │ ├── provider/ # Gestión de estado
│ │ └── widgets/ # Widgets específicos
│ │
│ ├── initialization/ # 🚀 Feature de inicialización
│ │ └── presentation/
│ │ ├── screens/
│ │ │ └── splash_screen.dart # Pantalla de splash
│ │ └── provider/
│ │ └── splash_provider.dart # Lógica de inicialización
│ │
│ ├── error_page/ # ⚠️ Feature de páginas de error
│ │ └── presentation/
│ │ ├── screens/
│ │ │ ├── error_screen.dart # Pantalla de error genérico
│ │ │ ├── not_found_screen.dart # Error 404
│ │ │ └── unauthorized_screen.dart # Error 401
│ │ └── provider/
│ │
│ └── common/ # Recursos comunes entre features
│ ├── data/ # Modelos compartidos
│ ├── domain/ # Lógica compartida
│ └── presentation/ # UI compartida
│ ├── screens/
│ └── provider/
│
├── assets/ # Recursos estáticos
│ ├── config/ # Archivos de configuración
│ ├── images/ # Imágenes e íconos
│ └── mocks/ # Datos JSON mock
│ ├── auth/ # Mocks de autenticación
│ └── user/ # Mocks de usuarios
│
└── main.dart # Punto de entrada de la aplicación
Este proyecto implementa Clean Architecture con tres capas principales más una capa transversal:
- Ubicación:
features/[feature]/domain/ - Responsabilidad: Lógica de negocio pura, independiente de frameworks y tecnologías externas
- Contiene:
entities/: Objetos de negocio (clases POJO/modelos inmutables)repositories/: Interfaces (contratos) de repositorios - define QUÉ hacer, no CÓMOusecases/: Casos de uso (acciones específicas del negocio) - orquesta la lógica
- Ejemplo:
UserEntity,AuthRepository(interface),LoginUseCase - Reglas: NO debe depender de ninguna otra capa, es el corazón de la aplicación
- Ubicación:
features/[feature]/data/ - Responsabilidad: Gestión de datos y comunicación con servicios externos
- Contiene:
sources/: Implementaciones de fuentes de datos (API REST, GraphQL, BD local, cache)models/: DTOs (Data Transfer Objects) que convierten JSON/datos externos a entidadesrepositories/: Implementaciones concretas de las interfaces del dominio
- Ejemplo:
AuthApi,LoginResponseModel,AuthRepositoryImpl - Reglas: Depende de Domain, implementa sus contratos, maneja serialización/deserialización
- Ubicación:
features/[feature]/presentation/ - Responsabilidad: UI y gestión de estado - todo lo que el usuario ve e interactúa
- Contiene:
screens/: Pantallas completas (pages)provider/: Gestores de estado (Provider, Riverpod, Bloc, Cubit, etc.)widgets/: Componentes UI reutilizables específicos del feature
- Ejemplo:
LoginScreen,AuthProvider,LoginBackgroundWidget - Reglas: Depende de Domain (usa casos de uso), NO debe depender de Data directamente
- Ubicación:
core/ - Responsabilidad: Funcionalidad compartida entre todos los features
- Contiene:
api/: Cliente HTTP base (Dio configurado con interceptores)config/: Configuraciones globales (entornos, feature flags)constants/: Constantes y endpointscommon/: Entidades y providers compartidostheme/: Temas y estilos de la aplicaciónui/widgets/: Widgets reutilizables globalesutils/: Utilidades (formateo, validación, helpers)router/: Sistema de navegaciónmock/: Datos de prueba
- Reglas: Puede ser usado por cualquier feature, debe ser genérico y reutilizable
┌─────────────────────────────────────────────────────────────┐
│ PRESENTATION LAYER │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Screen │───▶│ Provider │───▶│ UseCase │ │
│ │ (UI) │◀───│ (State Mgmt)│◀───│ (Domain) │ │
│ └─────────────┘ └──────────────┘ └──────────────┘ │
└────────────────────────────────┬────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ DOMAIN LAYER │
│ ┌──────────────┐ ┌─────────────────────────────────┐ │
│ │ UseCase │───▶│ Repository (Interface) │ │
│ │ │◀───│ │ │
│ └──────────────┘ └─────────────────────────────────┘ │
└────────────────────────────────┬────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ DATA LAYER │
│ ┌──────────────────┐ ┌───────────┐ ┌────────────┐ │
│ │ Repository Impl │───▶│ Model │───▶│ Source │ │
│ │ │◀───│ (DTO) │◀───│ (API/DB) │ │
│ └──────────────────┘ └───────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────┘
Este proyecto incluye varios features completamente funcionales que sirven como referencia:
Sistema completo de autenticación con múltiples implementaciones de gestión de estado:
-
Screens:
login_provider_screen.dart- Login con Provider (ChangeNotifier)login_riverpod_screen.dart- Login con Riverpod (StateNotifier)login_riverpod_new_screen.dart- Login con Riverpod AsyncNotifier
-
State Management: Tres implementaciones diferentes para comparar enfoques
- Provider (ChangeNotifier): Más simple, ideal para proyectos pequeños
- Riverpod (StateNotifier): Más robusto, sin context
- Riverpod (AsyncNotifier): Para operaciones asíncronas complejas
-
Data Sources:
auth_api.dart- Comunicación con API de autenticación -
Models:
login_response_model.dart,auth_state.dart -
Widgets: Fondo animado, contenedor de tarjeta personalizado
Múltiples diseños de pantalla principal para diferentes necesidades:
home_dashboard_screen.dart- Dashboard con métricas y estadísticas
home_grid_screen.dart- Vista en cuadrícula
home_categori_screen.dart- Vista por categorías
home_bento_box_screen.dart- Diseño Bento Box moderno
home_radial_screen.dart- Visualización radial con Syncfusion Charts
selection_business_grid_screen.dart- Selector de negocios en grid
selection_bussiness_screen.dart- Selector de negocios pero como una lista,lamentablemente no saque imagen de esto jaja.
Gestión del flujo de inicialización de la aplicación:
- Splash Screen: Pantalla de carga inicial
- Splash Provider: Lógica de redirección
- Verifica si hay sesión activa
- Redirige a Login o Home según corresponda
- Carga configuraciones iniciales
Páginas de error profesionales para diferentes casos:
error_screen.dart- Error genérico 500not_found_screen.dart- Error 404 (página no encontrada)unauthorized_screen.dart- Error 401 (no autorizado)
base_api_service.dart- Servicio base con Dio configuradoapi_client.dart- Cliente HTTP con interceptores- Manejo automático de errores HTTP
- Timeout configurado
- Headers personalizados
app_theme.dart- Sistema de temas personalizado- Colores, tipografías y estilos consistentes
- Soporte para tema claro (dark mode configurable)
app_router.dart- Sistema de navegación- Rutas nominadas (named routes)
- Guards de autenticación
- Redirecciones automáticas
app_bar_widget.dart- AppBar consistentedrawer_widget.dart- Menú de navegación lateral
business_card_widget.dart- Tarjeta de negocio reutilizablelogout_button_widget.dart- Botón de cerrar sesión
console.dart- Logs mejorados para debuggingalerts.dart- Sistema de alertas consistente (usando rflutter_alert)text_helpers.dart- Funciones auxiliares para texto
session_provider.dart- Gestión de sesión de usuarioapp_state_provider.dart- Estado global de la aplicación- Persistencia de sesión
routes_mock.dart- Rutas y datos simulados- Archivos JSON en
assets/mocks/para desarrollo sin backend - Facilita testing y desarrollo offline
Cada capa tiene una responsabilidad única y bien definida:
- Domain: Reglas de negocio
- Data: Manejo de datos
- Presentation: UI y experiencia de usuario
Las dependencias apuntan hacia adentro (hacia el dominio):
Presentation → Domain ← Data
↓ ↑ ↓
Core ←───────┴────────┘
- El dominio NO conoce detalles de implementación
- Las capas externas dependen de las internas, NUNCA al revés
- Core es usado por todos pero no depende de features específicos
- Se usan interfaces/abstracciones en lugar de implementaciones concretas
- Los repositorios se definen como interfaces en Domain
- Las implementaciones están en Data
- Facilita cambiar implementaciones sin afectar la lógica de negocio
- Lógica de negocio aislada y 100% testeable
- Inyección de dependencias facilita el testing
- Uso de interfaces permite crear mocks fácilmente
- Separación clara entre lógica y UI
- Estructura modular por features
- Fácil agregar nuevas funcionalidades sin afectar las existentes
- Bajo acoplamiento entre módulos
- Alto cohesión dentro de cada feature
- Código organizado y fácil de encontrar
- Convenciones de nomenclatura consistentes
- Separación clara de responsabilidades
- Documentación en el código
Si estás empezando un proyecto Flutter desde cero, sigue este orden recomendado (basado en Read_plan_Dev.md):
- Crear estructura de carpetas según lo definido en este README
- Definir el Tema (
core/theme/app_theme.dart) - Define colores y tipografías desde el inicio - Configurar Router (
core/router/app_router.dart) - Define rutas básicas:/,/login,/home
- BaseApiService con Dio (
core/api/base_api_service.dart) - Configura interceptores, timeout, headers - Crear Mocks (
assets/mocks/) - Prueba tus modelos sin backend- Archivos JSON para respuestas de API
- MockDataSources para testing
- Diseño del Login (UI primero) - Screens y Widgets
- Lógica de Auth - Model → Repository → UseCase → Provider
- Splash Screen - Lógica de redirección (¿Está logueado? → Home : Login)
- Páginas de Error - 404, 500, 401
| Orden | Tarea Crítica | Ubicación Clave |
|---|---|---|
| 1 | Estructura de carpetas | lib/features/, lib/core/ |
| 2 | Tema Global (Colores/Fuentes) | lib/core/theme/ |
| 3 | Router (Rutas iniciales) | lib/core/router/ |
| 4 | BaseApiService (Dio Setup) | lib/core/api/ |
| 5 | UI de Login & Errors (Diseño) | .../presentation/screens/ |
| 6 | Lógica de Features (Mocks o API) | .../data/ & .../domain/ |
| 7 | Splash & Redirección | lib/features/initialization/ |
- Flutter SDK ^3.9.2
- Dart SDK ^3.9.2
# Clonar el repositorio
git clone <repository-url>
# Navegar al directorio
cd base_flutter
# Instalar dependencias
flutter pub get
# Ejecutar la aplicación
flutter run
# Para un dispositivo específico
flutter run -d <device_id>
# Ver dispositivos disponibles
flutter devicesEste proyecto ya incluye las siguientes dependencias configuradas:
dependencies:
flutter:
sdk: flutter
# UI
cupertino_icons: ^1.0.8 # Iconos iOS
# State Management
provider: ^6.1.5+1 # Provider para gestión de estado
flutter_riverpod: ^3.3.1 # Riverpod para gestión de estado avanzada
# Networking
http: ^1.5.0 # Cliente HTTP básico
dio: ^5.9.2 # Cliente HTTP avanzado con interceptores
# UI Components
rflutter_alert: ^2.0.7 # Alertas personalizadas
syncfusion_flutter_gauges: ^33.1.46 # Medidores y gauges
syncfusion_flutter_charts: ^33.1.46 # Gráficos profesionales
# Tools
flutter_launcher_icons: ^0.14.4 # Generador de íconosdev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0 # Linting rules
http_mock_adapter: ^0.6.1 # Mock de HTTP para testing
change_app_package_name: ^1.1.0 # Cambiar package nameSi necesitas extender la funcionalidad, considera agregar:
dependencies:
# Local Storage
shared_preferences: ^2.2.2 # Almacenamiento key-value simple
hive: ^2.2.3 # Base de datos NoSQL local
hive_flutter: ^1.1.0
sqflite: ^2.3.0 # SQLite local
# Dependency Injection
get_it: ^7.6.7 # Service Locator
injectable: ^2.3.2 # DI con code generation
# Navigation
go_router: ^13.2.0 # Router declarativo avanzado
# Utilities
equatable: ^2.0.5 # Comparación de objetos
freezed_annotation: ^2.4.1 # Clases inmutables
json_annotation: ^4.8.1 # Serialización JSON
dartz: ^0.10.1 # Either<Failure, Success>
# Retrofit (API con generación de código)
retrofit: ^4.0.3
dev_dependencies:
# Code Generation
build_runner: ^2.4.8
freezed: ^2.4.7
json_serializable: ^6.7.1
injectable_generator: ^2.4.1
retrofit_generator: ^8.0.6
hive_generator: ^2.0.1
# Testing
mockito: ^5.4.4
bloc_test: ^9.1.5 # Testing para Bloc- Archivos:
snake_case→login_screen.dart,user_repository.dart - Carpetas:
snake_case→data/,presentation/,domain/
- Entities: Sustantivos en singular (
User,Product,Business) - Models: Sustantivo + "Model" (
UserModel,ProductModel,LoginResponseModel) - Use Cases: Verbo + sustantivo + "UseCase" (
GetUserUseCase,LoginUseCase,LogoutUseCase) - Repositories: Sustantivo + "Repository" (
UserRepository,AuthRepository)- Interface (Domain):
AuthRepository(abstracta) - Implementación (Data):
AuthRepositoryImpl
- Interface (Domain):
- Data Sources: Descriptivo + tipo (
AuthApi,UserLocalDataSource,CacheService) - Providers: Feature + "Provider" o Feature + tipo estado (
AuthProvider,AuthRiverpod,UserNotifier) - Screens: Descriptivo + "Screen" (
LoginScreen,HomeScreen,NotFoundScreen) - Widgets: Descriptivo + "Widget" (
LoginBackgroundWidget,BusinessCardWidget)
- Variables:
camelCase→userName,isLoggedIn,apiClient - Constantes:
SCREAMING_SNAKE_CASE→API_BASE_URL,MAX_RETRY_ATTEMPTS - Constantes de clase:
camelCase→static const String baseUrl = '...'
Cada feature DEBE ser autocontenido y seguir esta estructura:
feature_name/
├── data/ # Capa de datos
│ ├── sources/ # Fuentes de datos (API, DB, Cache)
│ ├── models/ # DTOs - Conversión de datos externos
│ └── repositories/ # Implementación de repositorios
│
├── domain/ # Capa de dominio (opcional si es muy simple)
│ ├── entities/ # Entidades del negocio
│ ├── repositories/ # Interfaces de repositorios
│ └── usecases/ # Casos de uso
│
└── presentation/ # Capa de presentación
├── screens/ # Pantallas
├── provider/ # Gestión de estado
└── widgets/ # Componentes UI específicos
Orden de imports:
- Dart SDK
- Flutter SDK
- Packages externos
- Archivos del proyecto
// 1. Dart
import 'dart:async';
// 2. Flutter
import 'package:flutter/material.dart';
// 3. Packages
import 'package:provider/provider.dart';
import 'package:dio/dio.dart';
// 4. Proyecto
import 'package:hm_flutter_base/core/api/base_api_service.dart';
import 'package:hm_flutter_base/features/auth/domain/entities/user.dart';class ExampleClass {
// 1. Constantes estáticas
static const String constantValue = 'value';
// 2. Variables estáticas
static int staticVariable = 0;
// 3. Variables de instancia privadas
final String _privateField;
// 4. Variables de instancia públicas
final String publicField;
// 5. Constructor
ExampleClass({required this.publicField, required String privateField})
: _privateField = privateField;
// 6. Métodos públicos
void publicMethod() {}
// 7. Métodos privados
void _privateMethod() {}
// 8. Getters y Setters
String get privateField => _privateField;
}Provider (ChangeNotifier):
class AuthProvider extends ChangeNotifier {
bool _isLoading = false;
bool get isLoading => _isLoading;
Future<void> login(String email, String password) async {
_isLoading = true;
notifyListeners();
// Lógica de login
_isLoading = false;
notifyListeners();
}
}Riverpod (StateNotifier):
final authProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
return AuthNotifier();
});
class AuthNotifier extends StateNotifier<AuthState> {
AuthNotifier() : super(AuthState.initial());
Future<void> login(String email, String password) async {
state = state.copyWith(isLoading: true);
// Lógica de login
state = state.copyWith(isLoading: false, user: user);
}
}try {
final result = await apiCall();
return Right(result); // Si usas dartz
} on DioException catch (e) {
return Left(NetworkFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}/// Autentica un usuario con email y contraseña.
///
/// Retorna [User] si la autenticación es exitosa.
/// Lanza [AuthException] si las credenciales son inválidas.
Future<User> login({
required String email,
required String password,
}) async {
// Implementación
}assets/
├── config/ # Archivos de configuración (.json)
├── images/ # Imágenes (.png, .jpg, .svg)
│ ├── icons/ # Íconos específicos
│ └── backgrounds/ # Fondos
└── mocks/ # Datos mock (.json)
├── auth/
└── user/
test/
├── features/
│ └── auth/
│ ├── data/
│ │ └── repositories/
│ │ └── auth_repository_impl_test.dart
│ ├── domain/
│ │ └── usecases/
│ │ └── login_usecase_test.dart
│ └── presentation/
│ └── provider/
│ └── auth_provider_test.dart
└── core/
└── utils/
└── text_helpers_test.dart
# Ejecutar todos los tests
flutter test
# Ejecutar tests específicos
flutter test test/features/auth/
# Ejecutar tests con coverage
flutter test --coverage
# Ver reporte de coverage (requiere lcov instalado)
genhtml coverage/lcov.info -o coverage/html
open coverage/html/index.htmlvoid main() {
group('AuthRepository', () {
late MockAuthApi mockAuthApi;
late AuthRepositoryImpl repository;
setUp(() {
mockAuthApi = MockAuthApi();
repository = AuthRepositoryImpl(api: mockAuthApi);
});
test('login should return User when successful', () async {
// Arrange
when(mockAuthApi.login(any, any))
.thenAnswer((_) async => LoginResponseModel(...));
// Act
final result = await repository.login('test@test.com', 'password');
// Assert
expect(result.isRight(), true);
});
});
}# Cambiar el nombre del paquete (Android e iOS)
flutter pub run change_app_package_name:main com.tu_empresa.tu_app# Configurar en pubspec.yaml y ejecutar
flutter pub run flutter_launcher_iconsCrea archivos de configuración en assets/config/:
config.dev.json- Desarrolloconfig.staging.json- Stagingconfig.prod.json- Producción
{
"apiBaseUrl": "https://api.example.com",
"environment": "development",
"enableLogging": true
}- Fork el proyecto
- Crea una rama para tu feature (
git checkout -b feature/nueva-funcionalidad) - Sigue los principios de Clean Architecture
- Mantén las convenciones de código
- Escribe tests para tu código
- Asegúrate de que todos los tests pasen (
flutter test) - Commit tus cambios (
git commit -m 'Add: nueva funcionalidad') - Push a la rama (
git push origin feature/nueva-funcionalidad) - Crea un Pull Request
Tipo: Descripción corta
Descripción detallada (opcional)
Tipos:
- Add: Nueva funcionalidad
- Update: Actualización de funcionalidad existente
- Fix: Corrección de bug
- Refactor: Refactorización de código
- Docs: Documentación
- Test: Tests
- Style: Formato, no afecta lógica
Este proyecto está bajo la licencia especificada en el archivo LICENSE.
- Empieza simple: Usa Provider si eres nuevo, después migra a Riverpod
- Entiende los widgets: Flutter es todo sobre widgets, aprende los básicos primero
- Hot Reload es tu amigo: Aprovecha el hot reload para iterar rápidamente
- Usa el DevTools: Flutter DevTools es excelente para debugging
- No sobre-ingenierices: Si tu app es muy simple, no necesitas todas las capas
- Empieza con un feature: Implementa auth completamente antes de seguir
- Los use cases son opcionales: Si la lógica es simple, el provider puede llamar al repository directamente
- Itera y refactoriza: Está bien empezar simple e ir agregando capas cuando las necesites
- ✅ Usa
constconstructors cuando sea posible (performance) - ✅ Mantén los widgets pequeños y reutilizables
- ✅ Separa lógica de UI (no pongas lógica de negocio en widgets)
- ✅ Usa named parameters para claridad
- ✅ Maneja errores apropiadamente (try-catch, Either)
- ✅ Escribe tests para lógica de negocio crítica
- ❌ No uses setState en StatelessWidget
- ❌ No pongas lógica de negocio en widgets
- ❌ No hagas llamadas a API directamente desde widgets
¿Preguntas? ¿Sugerencias? Abre un issue en el repositorio.







