Jetpack Compose es un cambio de paradigma enorme en muchos aspectos. Cambia la forma de pensar en casi todos los puntos involucrados en el desarrollo de una App Android.
Y la navegación no iba a ser menos.
¿Cómo se navega en Jetpack Compose? ¿Qué opciones tenemos? ¿Cómo es de difícil?
Tras organizar nuestro código para ser más escalable, en este artículo vamos a ver una introducción a la navegación en Jetpack Compose, y cómo aplicarla a tus proyectos.

¡Atención! Si quieres acceder más rápido a todo el contenido en vídeo, organizado, con contenido extra, soporte y muchas más sorpresas, puedes apuntarte gratis a mi formación Compose Expert y ver gratis el primer módulo de más de 3 horas con todo lo necesario para empezar.
Navegación en Jetpack Compose: ¿Cambia todo?
Aquí, como en muchas otras cosas, vas a tener que tomar decisiones importantes.
Si lo piensas, la realidad es que Jetpack Compose no te impone reinventar la rueda en temas de navegación.
Cuando creas una pantalla con Compose, realmente utilizas el setContent { }
de la Activity
(hay algunas variantes para hacerlo también en Fragment
s), y por tanto, podríás navegar exactamente igual que lo hacías con el sistema clásico.
Cuando el usuario haga click sobre algún componente, puedes hacer un startActivity()
y navegar a una Activity
nueva.
Esto te lo cuento también por si ya tienes una App escrita con el sistema antiguo, que sepas que puedes integrar Compose en cualquier pantalla de esta forma.
Pero, por supuesto, existe otra opción
Navigation Compose: Navegación entre Composables
Si recuerdas, desde hace unos años, en la forma tradicional tenemos otro sistema de navegación: Jetpack Navigation.
Mediante un grafo de navegación, podemos navegar entre Fragments o Activities de forma más estructurada. Incluso, nos permite tener un App con una única Activity, y que toda la navegación se haga mediante fragments.
Existe una variante para Jetpack Compose (aún en el alfa en el momento de escribir este artículo) que se llama Compose Navigation.
Con ella, podemos conseguir el equivalente al Single Activity en el que navegamos de un fragment a otro, pero en este caso navegamos entre Composables.
Y esto es lo que te quiero comentar hoy
Dependencia de Navigation Compose
Para poder usarlo, necesitamos incluir la siguiente dependencia:
implementation "androidx.navigation:navigation-compose:2.4.0-alpha09"
Puedes ver la última versión aquí.
Configuración de Navigation Compose
Para poder utilizarlo, necesitamos configurar algunos elementos importantes que nos ayuden a trabajar con Navigation.
La primera parte es el NavHostController
. Para que que no se nos duplique, tenemos una función remember
(este concepto de remember
ya lo vimos en el artículo del estado en JetpackCompose) que nos va a devolver siempre el mismo navController
.
Como la navegación va a ser de nivel superior (es la navegación de toda la App), podemos inicializarla directamente en el setContent { }
de la MainActivity
. Luego mejoraremos esto un poco:
val navController = rememberNavController()
La segunda parte es el NavHost
. El NavHost
es el componente de Navigation Compose que va a definir el grafo de navegación. Aquí le diremos qué navController
va a usar, la pantalla de origen, y las pantallas a las que se puede navegar.
Para ello:
NavHost( navController = navController, startDestination = "main" ) { }
Dentro del bloque de código, definimos con composable
cada uno de los Composables a los que podemos navegar, y cuál es su ruta:
NavHost( navController = navController, startDestination = currentScreen ) { composable("main") { MainScreen() } composable("detail"){ DetailScreen() } }
La DetailScreen
aún no la teníamos creada. De momento, vamos a crear algo sencillo:
@Composable fun DetailScreen() { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Text( text = "Detail Screen", style = MaterialTheme.typography.h4 ) } }
¿Qué nos falta ahora? Saber cómo navegar entre los distintos elemento de navegación
Cómo navegar entre pantallas con Compose Navigation
Para navegar, necesitamos tener acceso al navController
, así que vamos a pasárselo a la MainScreen
:
fun MainScreen(navController: NavHostController) { MyMoviesApp { Scaffold( topBar = { MainAppBar() } ) { padding -> MediaList(navController, Modifier.padding(padding)) } } }
Verás que vas a tener que pasar el navController
por toda la jerarquía de Composable
s, lo cual no es muy interesante ni a nivel de reusabilidad ni de testeo.
Pero por simplificar, de momento lo vamos a dejar así. Una vez estamos en el MediaListItem
, en la Column
principal solo tenemos que hacer:
Column( modifier = modifier .clickable { navController.navigate("detail") } )
Hacemos la columna clickable
gracias al modifier
, y en el click le decimos que navegue al detalle.
Pero aquí está fallando algo, ¿verdad? La DetailScreen
debería mostrar los detalles específicos de un elemento, y por tanto hay que pasarle un identificador como argumento.
Vamos a ver cómo hacerlo:
Cómo pasar argumentos a los Composables
Para identificar los argumentos que le pasamos a un Composable, lo hacemos a través de su route
o ruta de navegación. Es tan sencillo como añadir el argumento en la ruta, aunque hay que hacer algunas cosas que necesitas saber.
Lo primero es definir exactamente cómo va a ser la ruta de detalle:
composable("detail/{mediaId}") { DetailScreen() }
Si el argumento es obligatorio, se indica como ves arriba, tras una barra inclinada. Si puede ser opcional, se indica como un query param:
composable("detail?mediaId={mediaId}") { DetailScreen() }
En nuestro caso, como es obligatorio que el detalle reciba un id, vamos a ir por el primer camino.
Por defecto, los argumentos son de tipo String
, pero podemos configurar el tipo de la siguiente forma:
composable( route = "detail/{mediaId}", arguments = listOf(navArgument("mediaId") { type = NavType.IntType }) ) { DetailScreen() }
El siguiente paso es recuperar el id para pasárselo al Composable de detalle. Para ello usamos el backStackEntry
que recibe la lambda de composable
y accedemos a sus argumentos.
Como los argumentos son nullables, es una buena práctica requerir que nos devuelva un valor, o si no lanzar una excepción:
composable( route = "detail/{mediaId}", arguments = listOf(navArgument("mediaId") { type = NavType.IntType }) ) { backStackEntry -> val id = backStackEntry.arguments?.getInt("mediaId") requireNotNull(id) DetailScreen(id) }
Luego, en el click de los elementos de la lista, recuperamos el id y lo enviamos:
Column( modifier = modifier .clickable { navController.navigate("detail/${mediaItem.id}") } ) { ... }
Pruébalo, verás que ya puedes navegar a la pantalla de detalle.
Creando la pantalla de detalle
De momento hemos hecho algo muy básico, pero te dejo aquí como ejercicio pintar algo un poco mejor, donde se muestre una barra superior con el nombre del item, y la foto debajo, con el icono de vídeo si procede.
Te dejo aquí el código. El composable Thumb
ya lo teníamos extraído en la pantalla principal.
@Composable fun DetailScreen(mediaId: Int) { val mediaItem = remember { getMedia().first { it.id == mediaId } } Scaffold( topBar = { TopAppBar(title = { Text(text = mediaItem.title) }) } ) { Thumb(mediaItem = mediaItem) } }
Puedes ver todo el código en el repositorio. Echa un vistazo al commit de este artículo.
Mejorando el código
Ya tenemos todo funcionando, pero la realidad es que hay un cosas que son bastante mejorables.
- El
navController
no debería atravesar todo nuestra jerarquía de Composables, porque nos va a dar problemas tanto para testear como para las previews. Si has seguido el código, habrás visto que hemos tenido que desactivar una preview. - Tenemos hardcodeadas todas las rutas de navegación y sus argumentos. Esto no es muy escalable, y lo ideal es modelar esa navegación en objetos de alguna forma
- No hemos visto cómo navegar hacia atrás en la vista de detalle
Todo esto te lo voy a explicar en un vídeo gratuito exclusivo de mi formación Compose Expert. Puedes apuntarte gratis ya mismo para ir a verlo.

Apúntate ahora a Compose Expert y accede a más de 3h de contenido gratuito, soporte, extras y mucho más totalmente gratis.
En el siguiente artículo, vas a ver cómo utilizar las Cards de Material Design en Jetpack Compose.
0 comentarios