Cuando uses el DSL de Kotlin para construir tu gráfico, mantener los destinos y los eventos de navegación en un solo archivo puede ser difícil de mantener. Este es especialmente si tienes varios atributos independientes.
Extrae destinos
Debes mover tus destinos a la extensión NavGraphBuilder
.
funciones. Deben vivir cerca de las rutas que los definen, y
pantallas que muestran. Por ejemplo, considera el siguiente código a nivel de la aplicación
que crea un destino que muestra una lista de contactos:
// MyApp.kt
@Serializable
object Contacts
@Composable
fun MyApp() {
...
NavHost(navController, startDestination = Contacts) {
composable<Contacts> { ContactsScreen( /* ... */ ) }
}
}
Debes mover el código específico de navegación a un archivo separado:
// ContactsNavigation.kt
@Serializable
object Contacts
fun NavGraphBuilder.contactsDestination() {
composable<Contacts> { ContactsScreen( /* ... */ ) }
}
// MyApp.kt
@Composable
fun MyApp() {
...
NavHost(navController, startDestination = Contacts) {
contactsDestination()
}
}
Las definiciones de rutas y destinos ahora están separadas de la app principal y
puedes actualizarlos de forma independiente. La app principal solo depende de un
función de extensión. En este caso, es
NavGraphBuilder.contactsDestination()
La función de extensión NavGraphBuilder
forma el puente entre una red sin estado
función de componibilidad a nivel de la pantalla y lógica específica de Navigation. Esta capa puede
y definir de dónde proviene el estado
y cómo manejar los eventos.
Ejemplo
El siguiente fragmento de código presenta un nuevo destino para mostrar la dirección de correo electrónico de un contacto y actualiza el destino de la lista de contactos existente para exponer una evento de navegación para mostrar los detalles del contacto.
Este es un conjunto típico de pantallas que pueden ser internal
en su propio módulo.
que otros módulos no puedan acceder a ellos:
// ContactScreens.kt
// Displays a list of contacts
@Composable
internal fun ContactsScreen(
uiState: ContactsUiState,
onNavigateToContactDetails: (contactId: String) -> Unit
) { ... }
// Displays the details for an individual contact
@Composable
internal fun ContactDetailsScreen(contact: ContactDetails) { ... }
Cómo crear destinos
La siguiente función de extensión NavGraphBuilder
crea un destino.
que muestra el elemento ContactsScreen
componible. Además, ahora conecta
la pantalla con un ViewModel
que proporciona el estado de la IU de la pantalla y controla la
la lógica empresarial relacionada con la pantalla.
Los eventos de navegación, como la navegación al destino de los detalles de contacto, se
que están expuestos al llamador, en lugar de que ViewModel
los controle.
// ContactsNavigation.kt
@Serializable
object Contacts
// Adds contacts destination to `this` NavGraphBuilder
fun NavGraphBuilder.contactsDestination(
// Navigation events are exposed to the caller to be handled at a higher level
onNavigateToContactDetails: (contactId: String) -> Unit
) {
composable<Contacts> {
// The ViewModel as a screen level state holder produces the screen
// UI state and handles business logic for the ConversationScreen
val viewModel: ContactsViewModel = hiltViewModel()
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
ContactsScreen(
uiState,
onNavigateToContactDetails
)
}
}
Puedes usar el mismo enfoque para crear un destino que muestre las
ContactDetailsScreen
En este caso, en lugar de obtener el estado de la IU desde un
de vista, puedes obtenerlo directamente de NavBackStackEntry
.
// ContactsNavigation.kt
@Serializable
internal data class ContactDetails(val id: String)
fun NavGraphBuilder.contactDetailsScreen() {
composable<ContactDetails> { navBackStackEntry ->
ContactDetailsScreen(contact = navBackStackEntry.toRoute())
}
}
Cómo encapsular eventos de navegación
Así como encapsulas los destinos, puedes encapsular
de navegación para evitar exponer los tipos de rutas innecesariamente. Hazlo antes del
Crear funciones de extensión en NavController
// ContactsNavigation.kt
fun NavController.navigateToContactDetails(id: String) {
navigate(route = ContactDetails(id = id))
}
Reúnelo en un solo lugar
El código de navegación para mostrar contactos ahora está claramente separado del gráfico de navegación de tu app. La app debe hacer lo siguiente:
- Llama a funciones de extensión de
NavGraphBuilder
para crear destinos - Para conectar esos destinos, llama a las funciones de extensión de
NavController
para eventos de navegación
// MyApp.kt
@Composable
fun MyApp() {
...
NavHost(navController, startDestination = Contacts) {
contactsDestination(onNavigateToContactDetails = { contactId ->
navController.navigateToContactDetails(id = contactId)
})
contactDetailsDestination()
}
}
Resumen
- Ubica tu código de navegación para un conjunto relacionado de pantallas. en un archivo separado
- Crea funciones de extensión en
NavGraphBuilder
para exponer destinos - Crea funciones de extensión en
NavController
para exponer los eventos de navegación. - Usa
internal
para mantener la privacidad de las pantallas y los tipos de rutas