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
18:48 min - 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
Viendo ahora - 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
Gestión de Acciones y Estados en ViewModel de Android
Resumen
Conectar la UI con la lógica de negocio en Android requiere algo más que pintar pantallas: necesitas un ViewModel que reaccione a las acciones del usuario, actualice estados y dispare eventos puntuales. Aquí verás cómo construirlo paso a paso usando sealed interfaces, Channels y Flows sobre una pantalla de tareas en Jetpack Compose.
¿Cómo se separan acciones, estados y eventos en un ViewModel?
La diferencia entre estos tres conceptos es clave para mantener tu código limpio y predecible. Las acciones son lo que el usuario hace, los estados describen lo que la pantalla muestra, y los eventos son señales puntuales que ocurren una sola vez.
¿Qué diferencia hay entre un estado y un evento en Android? El estado persiste y se mantiene durante recomposiciones, mientras que un evento se emite una sola vez, ideal para mostrar un toast o disparar navegación sin que se repita.
En la implementación [01:55] se extrae el HomeDataState a su propia data class para tener un proyecto mejor organizado, en lugar de dejarlo flotando dentro del archivo de la pantalla.
¿Cómo modelar las acciones del usuario con sealed interface?
Una sealed interface permite agrupar todas las acciones posibles de una pantalla en un solo tipo cerrado, lo que facilita usar when exhaustivo. En el ejemplo se crea HomeScreenAction con:
OnToggleTask(val task: Task): marca o desmarca una tarea como completada.OnDeleteTask(val task: Task): borra una tarea individual.OnDeleteAllTask: borra todas las tareas (no necesita data, por eso es data object).OnAddTask: navega a la pantalla de creación o edición.
La regla práctica es simple: si la acción carga información, usas data class; si no, usas data object.
¿Y los eventos cómo se modelan?
Los eventos viven en otra sealed class llamada HomeScreenEvent y se disparan cuando una acción se completa con éxito [04:30]. Aquí se definen tres: UpdatedTask, DeleteAllTask y DeleteTask. Cada uno tiene un mensaje distinto en la UI, por eso se separan.
¿Cómo construir el HomeScreenViewModel paso a paso?
La clase hereda de ViewModel y recibe un TaskLocalDataSourceFake como dependencia. Dentro mantienes dos cosas distintas: el estado expuesto y un canal privado para eventos.
El estado se declara con mutableStateOf(HomeDataState()) como variable privada con setter privado, expuesta solo para lectura. Los eventos viajan por un Channel<HomeScreenEvent> que se transforma con receiveAsFlow() para que la pantalla los consuma.
¿Por qué usar Channel para eventos en lugar de StateFlow? Porque los Channels garantizan que el evento se procese una sola vez. Si usas estado, una recomposición podría volver a disparar el toast o la navegación.
¿Cómo observar el flujo de tareas dentro del init?
En el bloque init se toma el taskLocalDataSource, se observan las tareas y en cada emisión se separan en dos listas usando filter: las que tienen isCompleted = true van a completedTask y el resto a pendingTask. Luego se actualiza el estado con state.copy(...) y todo se lanza dentro de viewModelScope.
¿Cómo procesar las acciones con un when?
La función onAction(action: HomeScreenAction) discrimina cada caso:
OnDeleteAllTask: llama ataskLocalDataSource.removeAllTasks()dentro de unviewModelScope.launchporque es una suspend function.OnDeleteTask: ejecutaremoveTask(action.task).OnToggleTask: crea unupdatedTask = action.task.copy(isCompleted = !action.task.isCompleted)y llama aupdate.OnAddTask: se deja en elelseporque se intercepta más adelante en la navegación.
Después de cada operación exitosa, se envía el evento correspondiente al eventChannel.
¿Qué es el patrón Root Composable y por qué usarlo?
Es un truco para separar el composable que conoce al ViewModel del que solo recibe estado y callbacks [10:45]. Creas un HomeScreenRoot que instancia el ViewModel con la función viewModel(), extrae estado y eventos, y llama al HomeScreen real pasándole solo lo necesario.
kotlin @Composable fun HomeScreenRoot(viewModel: HomeScreenViewModel = viewModel()) { HomeScreen( state = viewModel.state, events = viewModel.events, onAction = viewModel::onAction ) }
La ventaja es enorme: tus previews dejan de ser un infierno porque el HomeScreen puro recibe datos planos, sin depender del ViewModel.
¿Cómo conectar las acciones a la UI?
Cada interacción de la pantalla invoca onAction con la acción correspondiente. El ícono de borrar todo dispara OnDeleteAllTask y cierra el menú con isMenuExtended = false. Cada item de tarea recibe sus callbacks de onDelete y onToggle tanto en la lista de completadas como en la de pendientes.
¿Cómo escuchar eventos y mostrar toasts?
Dentro de HomeScreen usas un LaunchedEffect(true) para que se ejecute una sola vez y consumes el flow con events.collect. Un when discrimina cada evento y muestra un toast corto:
kotlin LaunchedEffect(true) { events.collect { event -> when (event) { DeleteAllTask -> Toast.makeText(context, context.getString(R.string.all_tasks_deleted), Toast.LENGTH_SHORT).show() } } }
El contexto se obtiene con LocalContext.current y los textos se centralizan como recursos string para mantener la app traducible.
¿Cómo calcular el resumen y el progreso de tareas?
Desde el ViewModel puedes derivar el tamaño de cada lista. En la sección summaryInfo envías completedTask = state.completedTask.size y totalTask = state.completedTask.size + state.pendingTask.size. Con eso, la barra de progreso se actualiza automáticamente cada vez que el usuario marca, desmarca o borra una tarea, llegando incluso a superar el 50 % visualmente cuando completas más de la mitad.
Ahora te toca a ti: comparte un pantallazo del estado de tu proyecto con todas las operaciones funcionando y cuéntanos en los comentarios qué parte del flujo acción-estado-evento te costó más entender.