Navegación entre módulos
Conecte funcionalidades sin acoplarlas
Navegación entre módulos es una lección gratuita de Android Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Android Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Android Academy incluye 4 lecciones en total.
El desafío de la navegación
Cuando las funcionalidades se encuentran en módulos separados, surge una nueva pregunta: ¿cómo puede :feature:home abrir una pantalla de :feature:profile sin depender de ella? Si las funcionalidades se importan directamente entre sí, se produce acoplamiento y existe riesgo de ciclos.
En esta lección conectará las funcionalidades mediante la navegación, manteniéndolas independientes.
Dónde reside el NavHost
El único NavHost reside en el módulo :app, el único lugar que puede conocer todas las funcionalidades. Cada funcionalidad aporta sus destinos y :app los ensambla en un único grafo.
// app/AppNavHost.kt
@Composable
fun AppNavHost(navController: NavHostController = rememberNavController()) {
NavHost(navController, startDestination = HomeRoute) {
homeScreen(onProfileClick = { navController.navigate(ProfileRoute) })
profileScreen(onBack = { navController.popBackStack() })
}
}Rutas con seguridad de tipos
Navigation Compose moderno admite rutas con seguridad de tipos: una ruta es un objeto o una clase de datos @Serializable, no una cadena mágica. Cada funcionalidad define su propio tipo de ruta en su módulo, por lo que es propietaria de su contrato de navegación.
// feature/profile/ProfileRoute.kt
import kotlinx.serialization.Serializable
@Serializable
data class ProfileRoute(val userId: String)Las funcionalidades exponen extensiones de NavGraphBuilder
La clave es que cada funcionalidad expone una función de extensión de NavGraphBuilder que registra su destino. La funcionalidad es propietaria de su pantalla; :app solo llama a esta función. La funcionalidad nunca hace referencia a otras funcionalidades.
// feature/profile/ProfileNavigation.kt
fun NavGraphBuilder.profileScreen(onBack: () -> Unit) {
composable<ProfileRoute> { backStackEntry ->
val route: ProfileRoute = backStackEntry.toRoute()
ProfileScreen(userId = route.userId, onBack = onBack)
}
}Desacoplar mediante callbacks de navegación
Una funcionalidad nunca debe llamar directamente a navigate(SomeOtherFeatureRoute), ya que eso exigiría depender de la otra funcionalidad. En su lugar, expone callbacks lambda como onProfileClick. El módulo :app decide a dónde conducen realmente.
// feature/home/HomeNavigation.kt
fun NavGraphBuilder.homeScreen(onProfileClick: (String) -> Unit) {
composable<HomeRoute> {
HomeScreen(onUserClick = { userId -> onProfileClick(userId) })
}
}
// :home does NOT know ProfileRoute exists.El módulo :app lo conecta todo
Solo el módulo :app conoce todas las rutas y conecta los callbacks con la navegación real. Este es el único lugar donde el acoplamiento es aceptable: su función completa consiste en ensamblar.
// app/AppNavHost.kt
NavHost(navController, startDestination = HomeRoute) {
homeScreen(
onProfileClick = { userId ->
navController.navigate(ProfileRoute(userId)) // app knows both
}
)
profileScreen(onBack = { navController.popBackStack() })
}Pasar argumentos de forma segura
Como las rutas son clases de datos @Serializable, los argumentos se comprueban en tiempo de compilación. Puede recuperarlos con toRoute() dentro del destino. Ya no tendrá que analizar cadenas ni enfrentarse a errores en tiempo de ejecución causados por arguments?.getString(...).
// Reading arguments inside the destination
composable<ProfileRoute> { entry ->
val args: ProfileRoute = entry.toRoute()
ProfileScreen(userId = args.userId)
}
// Or directly in a ViewModel via SavedStateHandle
val route: ProfileRoute = savedStateHandle.toRoute()Compartir tipos de ruta mediante un módulo api
En ocasiones, una funcionalidad realmente necesita navegar a otra y quiere utilizar su tipo de ruta. En lugar de depender de toda la funcionalidad, exponga únicamente la ruta en un pequeño módulo :feature:profile:api que contenga solo la ruta @Serializable. El módulo pesado :impl se mantiene privado.
// feature/profile/api -> only the route type
@Serializable
data class ProfileRoute(val userId: String)
// feature/home/build.gradle.kts
// home may depend on the lightweight api to build the route,
// but never on :feature:profile:impl
implementation(project(":feature:profile:api"))Navegación anidada por funcionalidad
Una funcionalidad con varias pantallas puede exponer un grafo anidado completo mediante navigation<T>. La funcionalidad controla su flujo interno; :app solo monta el grafo en un punto de entrada.
// feature/onboarding/OnboardingNavigation.kt
fun NavGraphBuilder.onboardingGraph(onFinished: () -> Unit) {
navigation<OnboardingGraph>(startDestination = WelcomeRoute) {
composable<WelcomeRoute> { WelcomeScreen() }
composable<PermissionsRoute> { PermissionsScreen(onDone = onFinished) }
}
}Enlaces profundos entre módulos
Las rutas con seguridad de tipos también admiten enlaces profundos. Una funcionalidad declara un patrón de URI de enlace profundo para su destino; el NavHost de :app resuelve un enlace entrante y lo dirige a la pantalla correcta de la funcionalidad, sin necesidad de importar otra funcionalidad.
composable<ProfileRoute>(
deepLinks = listOf(
navDeepLink<ProfileRoute>(basePath = "https://myapp.com/profile")
)
) { entry ->
ProfileScreen(userId = entry.toRoute<ProfileRoute>().userId)
}Resumen del patrón de desacoplamiento
En conjunto, las reglas son sencillas:
- Cada funcionalidad es propietaria de su tipo de ruta y de una extensión de NavGraphBuilder.
- Las funcionalidades comunican su intención mediante callbacks lambda, no mediante navegación directa.
- Solo :app conoce todas las funcionalidades y conecta los callbacks con las rutas reales.
- Si es necesario compartir una ruta, expóngala mediante un pequeño módulo :api.
Así las funcionalidades se mantienen independientes, pueden aprovechar la caché de compilación y no contienen ciclos.
Comprobación rápida
En una aplicación con varios módulos, :feature:home debe enviar al usuario a una pantalla de :feature:profile. ¿Cuál es la forma más limpia de mantener desacopladas las funcionalidades?
Resumen: navegación entre módulos
Ha aprendido a conectar funcionalidades sin acoplarlas:
- El único
NavHostreside en el módulo :app, que es ligero. - Cada funcionalidad es propietaria de una ruta
@Serializabley de una extensión deNavGraphBuilder. - Las funcionalidades exponen callbacks en lugar de navegar directamente a otras funcionalidades.
- Comparta una ruta únicamente mediante un pequeño módulo :api cuando sea realmente necesario; los enlaces profundos y los grafos anidados siguen el mismo patrón.
Con esto concluye Arquitectura de aplicaciones Android con varios módulos: ahora puede dividir, conectar y navegar por una base de código Android escalable.
Preguntas frecuentes
¿La lección «Navegación entre módulos» es gratis?
Sí — el texto completo de «Navegación entre módulos» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Android Academy, actualiza a CoddyKit PRO. El curso de Android Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Navegación entre módulos»?
Conecte funcionalidades sin acoplarlas Practicas Android Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Android Academy?
No se requiere experiencia previa. Android Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.
¿Cuánto tiempo toma la lección «Navegación entre módulos»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Android Academy?
Sí. Cada lección de Android Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Por qué modularizar
- Módulos de funcionalidades y del núcleo
- Gestión de dependencias entre módulos
- Navegación entre módulos