# Flutter Interview Challenge - Desafíos Técnicos ## Desafío Técnico: Sistema de Gestión de Tareas ### Contexto Desarrollar una aplicación Flutter para gestión de tareas con las siguientes características: ### Requisitos Funcionales 1. **CRUD de Tareas** - Crear nuevas tareas con título, descripción y fecha límite - Listar todas las tareas con estado (pendiente/completada) - Editar tareas existentes - Marcar tareas como completadas - Eliminar tareas 2. **Filtrado y Búsqueda** - Filtrar por estado (todas/pendientes/completadas) - Búsqueda por título - Ordenar por fecha de creación o fecha límite 3. **Persistencia Local** - Guardar datos localmente usando SQLite - Sincronizar con API REST (simulada) ### Requisitos Técnicos #### Arquitectura - Implementar **Clean Architecture** - Usar **BLoC/Cubit** para gestión de estado - Aplicar **Repository Pattern** - Implementar **Dependency Injection** con GetIt #### Estructura Esperada ``` lib/ ├── core/ │ ├── error/ │ ├── network/ │ └── database/ ├── features/ │ └── tasks/ │ ├── data/ │ │ ├── datasources/ │ │ ├── models/ │ │ └── repositories/ │ ├── domain/ │ │ ├── entities/ │ │ ├── repositories/ │ │ └── usecases/ │ └── presentation/ │ ├── bloc/ │ ├── pages/ │ └── widgets/ └── injection_container.dart ``` ### Modelos de Datos #### Task Entity ```dart class Task extends Equatable { final String id; final String title; final String description; final DateTime createdAt; final DateTime? dueDate; final bool isCompleted; const Task({ required this.id, required this.title, required this.description, required this.createdAt, this.dueDate, this.isCompleted = false, }); @override List get props => [id, title, description, createdAt, dueDate, isCompleted]; } ``` ### Casos de Uso a Implementar 1. **GetTasks** ```dart class GetTasks { final TaskRepository repository; Future>> call(TaskFilter filter); } ``` 2. **CreateTask** ```dart class CreateTask { final TaskRepository repository; Future> call(CreateTaskParams params); } ``` 3. **UpdateTask** ```dart class UpdateTask { final TaskRepository repository; Future> call(Task task); } ``` 4. **DeleteTask** ```dart class DeleteTask { final TaskRepository repository; Future> call(String taskId); } ``` ### UI Requirements #### Páginas Requeridas 1. **Task List Page** - Lista de tareas con SwipeToDelete - Floating Action Button para agregar - Search bar - Filter chips (All/Pending/Completed) 2. **Task Form Page** - Formulario para crear/editar - Validación de campos - Date picker para fecha límite 3. **Task Detail Page** - Vista completa de la tarea - Opciones de editar/eliminar #### Componentes UI ```dart class TaskCard extends StatelessWidget { final Task task; final VoidCallback onTap; final VoidCallback onToggle; final VoidCallback onDelete; @override Widget build(BuildContext context) { return Dismissible( key: Key(task.id), background: Container(color: Colors.red), onDismissed: (_) => onDelete(), child: Card( child: ListTile( leading: Checkbox( value: task.isCompleted, onChanged: (_) => onToggle(), ), title: Text(task.title), subtitle: Text(task.description), trailing: task.dueDate != null ? Text(DateFormat.yMd().format(task.dueDate!)) : null, onTap: onTap, ), ), ); } } ``` ### Gestión de Estado con BLoC #### TaskBloc Events ```dart abstract class TaskEvent extends Equatable {} class LoadTasks extends TaskEvent { final TaskFilter filter; const LoadTasks(this.filter); @override List get props => [filter]; } class CreateNewTask extends TaskEvent { final CreateTaskParams params; const CreateNewTask(this.params); @override List get props => [params]; } class UpdateExistingTask extends TaskEvent { final Task task; const UpdateExistingTask(this.task); @override List get props => [task]; } class DeleteExistingTask extends TaskEvent { final String taskId; const DeleteExistingTask(this.taskId); @override List get props => [taskId]; } class ToggleTaskCompletion extends TaskEvent { final String taskId; const ToggleTaskCompletion(this.taskId); @override List get props => [taskId]; } ``` #### TaskBloc States ```dart abstract class TaskState extends Equatable {} class TaskInitial extends TaskState { @override List get props => []; } class TaskLoading extends TaskState { @override List get props => []; } class TaskLoaded extends TaskState { final List tasks; final TaskFilter currentFilter; const TaskLoaded(this.tasks, this.currentFilter); @override List get props => [tasks, currentFilter]; } class TaskError extends TaskState { final String message; const TaskError(this.message); @override List get props => [message]; } class TaskOperationSuccess extends TaskState { final String message; const TaskOperationSuccess(this.message); @override List get props => [message]; } ``` ### Base de Datos Local #### Database Helper ```dart class DatabaseHelper { static const String tableTasks = 'tasks'; static const String columnId = 'id'; static const String columnTitle = 'title'; static const String columnDescription = 'description'; static const String columnCreatedAt = 'created_at'; static const String columnDueDate = 'due_date'; static const String columnIsCompleted = 'is_completed'; Future get database async { return await openDatabase( join(await getDatabasesPath(), 'tasks.db'), version: 1, onCreate: _onCreate, ); } Future _onCreate(Database db, int version) async { await db.execute(''' CREATE TABLE $tableTasks( $columnId TEXT PRIMARY KEY, $columnTitle TEXT NOT NULL, $columnDescription TEXT NOT NULL, $columnCreatedAt TEXT NOT NULL, $columnDueDate TEXT, $columnIsCompleted INTEGER NOT NULL DEFAULT 0 ) '''); } } ``` ### Testing Requirements #### Unit Tests ```dart // Test para TaskBloc void main() { group('TaskBloc', () { late TaskBloc taskBloc; late MockGetTasks mockGetTasks; late MockCreateTask mockCreateTask; setUp(() { mockGetTasks = MockGetTasks(); mockCreateTask = MockCreateTask(); taskBloc = TaskBloc( getTasks: mockGetTasks, createTask: mockCreateTask, updateTask: mockUpdateTask, deleteTask: mockDeleteTask, ); }); blocTest( 'emits [TaskLoading, TaskLoaded] when LoadTasks is added', build: () { when(mockGetTasks(any)) .thenAnswer((_) async => Right(tTaskList)); return taskBloc; }, act: (bloc) => bloc.add(LoadTasks(TaskFilter.all)), expect: () => [ TaskLoading(), TaskLoaded(tTaskList, TaskFilter.all), ], ); }); } ``` #### Widget Tests ```dart void main() { group('TaskCard Widget', () { testWidgets('should display task information correctly', (tester) async { await tester.pumpWidget( MaterialApp( home: Scaffold( body: TaskCard( task: tTask, onTap: () {}, onToggle: () {}, onDelete: () {}, ), ), ), ); expect(find.text(tTask.title), findsOneWidget); expect(find.text(tTask.description), findsOneWidget); expect(find.byType(Checkbox), findsOneWidget); }); }); } ``` ### Criterios de Evaluación #### Arquitectura (25%) - Separación correcta de capas - Implementación de Clean Architecture - Dependency Injection apropiada - Principios SOLID aplicados #### Gestión de Estado (25%) - Uso correcto de BLoC pattern - Manejo de estados apropiado - Events y States bien definidos #### UI/UX (20%) - Interfaz intuitiva y responsive - Animaciones y transiciones fluidas - Manejo de estados de loading/error - Cumplimiento de Material Design #### Calidad de Código (20%) - Código limpio y mantenible - Nomenclatura apropiada - Comentarios donde sea necesario - Manejo de errores robusto #### Testing (10%) - Cobertura de tests unitarios - Tests de widgets críticos - Mocks apropiados ### Bonus Points #### Funcionalidades Adicionales - Notificaciones push para tareas próximas a vencer - Categorización de tareas con colores - Sync con Google Calendar - Dark/Light theme toggle - Internationalization (i18n) #### Optimizaciones - Lazy loading para listas grandes - Cache inteligente - Optimistic updates - Pull-to-refresh ### Tiempo Estimado - **Básico**: 4-6 horas - **Con bonus**: 8-10 horas ### Entregables 1. Código fuente completo 2. README con instrucciones de setup 3. Lista de features implementadas 4. Screenshots de la aplicación funcionando 5. APK de prueba (opcional) ### Preguntas de Follow-up #### Técnicas 1. ¿Cómo manejarías la sincronización offline-online? 2. ¿Qué estrategias usarías para optimizar la performance? 3. ¿Cómo implementarías real-time updates? #### Arquitecturales 1. ¿Cómo escalarías esta app para múltiples usuarios? 2. ¿Qué patrones adicionales considerarías para features más complejas? 3. ¿Cómo implementarías A/B testing? #### UX/UI 1. ¿Cómo mejorarías la accesibilidad? 2. ¿Qué métricas usarías para medir el éxito de la app? 3. ¿Cómo manejarías diferentes tamaños de pantalla?