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
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
Navegación entre pantallas con NavHost
Resumen
Conectar dos pantallas en una app Android no es solo cuestión de mostrarlas, sino de orquestar el flujo de usuario. Aquí aprendes a usar NavHost y NavController en Jetpack Compose para navegar entre una lista de tareas y un formulario de creación, además de ordenar resultados por fecha. Es una guía pensada para desarrolladores Android que ya manejan Compose y quieren implementar navegación moderna con clases serializables.
¿Cómo se configura NavHost y NavController en Compose?
La idea es que tu Composable raíz, llamado NavigationRoot, reciba un NavHostController y declare cada destino como una clase serializable, no como un string. Este enfoque es la forma más reciente y segura de manejar rutas en Android.
Dentro de NavigationRoot defines un Box con Modifier.fillMaxSize() y, dentro, un NavHost que recibe el navController y un startDestination. Cada destino se modela así [00:35]:
kotlin @Serializable object HomeScreenDestination
@Serializable object TaskScreenDestination
El startDestination apunta a HomeScreenDestination, y dentro de las llaves del NavHost declaras cada composable asociado a su destino. El match entre el tipo del destino y el bloque composable es lo que conecta todo.
¿Qué es NavHostController en Jetpack Compose? Es el controlador que gestiona la pila de navegación. Lo creas con
rememberNavController()en tuMainActivityy lo pasas hacia abajo alNavigationRoot.
¿Dónde debo crear el NavController?
Un nivel arriba de NavigationRoot, en MainActivity. Ahí lo instancias con rememberNavController() y lo inyectas como parámetro. Mantenerlo en la raíz evita recreaciones innecesarias y centraliza el control del stack.
¿Cómo navegar entre Home Screen y Task Screen?
No basta con declarar destinos: necesitas disparar la navegación desde eventos de UI. La técnica consiste en exponer lambdas específicas en cada root composable y conectarlas al navController desde NavigationRoot.
En HomeScreenRoot agregas una lambda navigateToTaskScreen que se invoca cuando el usuario presiona el botón flotante de agregar tarea [02:10]. La interceptación del evento se hace en el onAction del composable:
kotlin when (action) { is HomeScreenAction.OnAddTask -> navigateToTaskScreen() else -> viewModel.onAction(action) }
Fíjate en algo importante: esta acción no pasa por el ViewModel. Cuando un evento tiene una correspondencia uno a uno con una navegación, tiene más sentido manejarlo directamente en el root y evitar lógica innecesaria en el ViewModel.
¿Cómo regreso a la pantalla anterior?
Desde TaskScreenRoot expones una lambda navigateBack que llama a navController.navigateUp(). Esto limpia el stack y te devuelve al home. La interceptación es simétrica: capturas la acción de back en el when y delegas el resto al ViewModel [04:20].
¿Cuándo usar navigateUp en lugar de navigate? Usa
navigateUp()cuando quieras regresar respetando la jerarquía del stack. Usanavigate()cuando quieras ir hacia adelante a un nuevo destino.
Un detalle fácil de olvidar: cuando guardas una tarea con éxito, también debes navegar de regreso. En el ViewModel, cuando se emite el estado taskCreated, invocas navigateBack() desde TaskScreen. Sin esto, el usuario queda atrapado en el formulario aunque la tarea ya esté guardada.
¿Cómo ordenar tareas por fecha en Kotlin?
Después de validar la navegación entre pantallas, surge un detalle de experiencia: las tareas recién creadas aparecen al final de la lista. Lo natural es verlas primero. Para resolverlo, agregas un campo date al modelo Task con tipo LocalDateTime e inicializado con LocalDateTime.now().
En el HomeViewModel aplicas el ordenamiento sobre las dos listas:
- Tareas pendientes filtradas y ordenadas con
sortByDescending { it.date }. - Tareas completadas ordenadas con el mismo criterio sobre
task.date. - El campo
datese asigna automáticamente al crear cualquier tarea nueva.
Al probar el flujo completo, una tarea recién creada aparece en la primera posición de la lista de pendientes [07:45]. Eso confirma que el ordenamiento descendente por fecha funciona.
¿Por qué usar LocalDateTime en lugar de timestamp?
LocalDateTime es más legible y se integra mejor con las APIs modernas de Java Time. Para una app local sin sincronización entre zonas horarias, simplifica el código sin perder precisión.
¿Qué logras con esta arquitectura de navegación?
Tienes dos pantallas conectadas con un flujo de usuario completo: listar, crear, guardar y volver, todo con ordenamiento por fecha. La separación entre lógica de navegación y lógica de negocio queda clara: el navController vive en la raíz, las lambdas viajan hacia abajo, y el ViewModel solo se ocupa de lo que es realmente estado.
Este patrón con clases @Serializable como destinos es lo que Google recomienda hoy para navegación type safe en Compose. Y la próxima parada es la edición de tareas, donde tendrás que pasar argumentos entre destinos.
¿Ya implementaste navegación con destinos serializables en tu app? Cuéntame qué problemas encontraste al migrar desde rutas con string.