Contenido del curso
Personalización de la UI con Material Design
Composición de layouts en Jetpack Compose
- 6

Modelo de datos para una to do app
Viendo ahora - 7

Creación de Componentes UI en Jetpack Compose: Summary y Task Item
17:36 min - 8

HomeScreen con Scaffold en Jetpack Compose
23:16 min - 9

Creación de un Arco de Progreso Animado en Compose
14:39 min - 10

Gestión de Acciones y Estados en ViewModel de Android
23:14 min - 11

Formateo y Ajustes Finales en Pantalla Home de To Do App
04:17 min
Construcción de funcionalidades
Navegación en Jetpack Compose
Creación de Bases de datos y dependencias
Finalización de la app
Modelo de datos para una to do app
Resumen
Antes de pintar pantallas en Jetpack Compose, conviene pensar la lógica: qué es una tarea, qué datos guarda y qué operaciones necesitas. Aquí armas el modelo de dominio de una to do app siguiendo clean architecture, defines la abstracción de la fuente de datos y construyes una implementación falsa en memoria con Flow para reaccionar a los cambios.
Esta guía te sirve si estás aprendiendo Android moderno con Kotlin y quieres entender cómo separar dominio, datos y UI sin saltarte pasos.
Cómo modelar una tarea con data class y enum en Kotlin
Lo primero es identificar qué representa una tarea dentro de tu aplicación. Para eso creas un paquete domain que albergará el modelo, y dentro defines la data class Task [01:00].
Una tarea no necesita 20 campos, necesita los justos:
- Un
idde tipoStringpara identificarla de forma única. - Un
titlede tipoString, obligatorio. - Una
descriptionde tipoString?, nulable, porque a veces solo quieres anotar el título. - Un
isCompleted: Booleancon valor por defectofalse. - Una
category: Category?opcional.
La categoría no es un string libre, es un enum class llamado Category con valores como WORK, PERSONAL, SHOPPING y OTHER [02:30]. Usar un enum te da seguridad de tipos y autocompletado, y evita errores tipográficos al asignar categorías.
¿Por qué usar una data class para Task? Porque Kotlin te genera automáticamente
equals,hashCode,copyytoString, lo que te ahorra código repetitivo cuando comparas o actualizas tareas en una lista.
Qué es TaskLocalDataSource y cómo definir las operaciones CRUD
Con el modelo listo, necesitas declarar qué puedes hacer con esas tareas. Aquí entra el patrón de abstracción: una interface llamada TaskLocalDataSource que describe el contrato sin atarse a una implementación concreta [03:45].
Las operaciones que define son las clásicas del CRUD:
addTask(task: Task)para crear.updateTask(task: Task)para actualizar.removeTask(task: Task)para borrar una.deleteAllTasks()para limpiar todo.getTaskById(id: String): Task?para consultar una específica.- Una propiedad
taskFlow: Flow<List<Task>>para observar la lista completa.
Todas las funciones se marcan como suspend, porque simulan operaciones asíncronas y eventualmente llamarán a delay, que solo puede invocarse desde otra función suspend o dentro de una coroutine.
Por qué exponer la lista como Flow y no como List
Un Flow te permite observar cambios en el tiempo. Si agregas, actualizas o borras una tarea, la UI recibe la nueva lista automáticamente sin que tengas que pedirla otra vez. Esto encaja perfecto con la recomposición de Compose.
Internamente vas a usar un MutableStateFlow<List<Task>> inicializado con emptyList(), y lo expones públicamente como Flow de solo lectura.
Cómo implementar FakeTaskLocalDataSource para simular una base de datos
La interfaz no hace nada por sí sola. Necesitas una implementación, y la primera versión va a ser falsa: vive solo en RAM. Por eso la clase se llama FakeTaskLocalDataSource [05:30] y se ubica en un paquete data.
Más adelante migrarás a Room, pero por ahora una lista mutable es suficiente para probar la lógica de la UI.
La implementación gira alrededor de un MutableStateFlow privado que guarda la lista actual. Cada operación sigue el mismo patrón:
- Toma el valor actual del flow y lo convierte a
MutableList. - Modifica la lista (agrega, actualiza o borra).
- Simula un retraso con
delaypara imitar I/O real. - Reasigna el nuevo valor al flow para notificar a los observadores.
¿Cómo actualizo una tarea por id en una lista? Usas
indexOfFirst { it.id == task.id }. Si devuelve un valor distinto de-1, reemplazas el elemento en esa posición. Si devuelve-1, la tarea no existe y no haces nada.
Para getTaskById el truco es usar taskFlow.value.firstOrNull { it.id == id }, que devuelve la tarea o null si no la encuentra. Por eso el tipo de retorno también es nulable.
Cómo probar el flow desde MainActivity con LaunchedEffect
Con la lógica armada, toca verificar que funciona. En MainActivity instancias el FakeTaskLocalDataSource y observas su flow desde un composable [09:15].
Los pasos clave dentro de tu pantalla son:
- Declarar
var text by remember { mutableStateOf("") }para guardar el contenido a mostrar. - Lanzar un
LaunchedEffect(true)que recolectetaskFlowy actualicetextcon cada emisión. - Lanzar otro
LaunchedEffectque llame aaddTaskcon una tarea de prueba, usandoUUID.randomUUID().toString()como id. - Pintar un
Text(text)dentro delScaffold.
Al correr la app verás el contenido de la lista renderizado en pantalla. Si el texto se solapa con la barra superior, aplicas el innerPadding que entrega el Scaffold al modifier del Text, y el margen queda respetado.
¿Qué hace LaunchedEffect en Compose? Ejecuta una coroutine atada al ciclo de vida del composable. Cuando la clave cambia, cancela la anterior y lanza una nueva. Con clave
truecorre una sola vez al entrar.
Con esta base ya tienes el dominio modelado, las operaciones definidas y una fuente de datos falsa que reacciona a cambios. ¿Tú qué categoría agregarías a tu propia to do app?