El siguiente código es del proyecto de muestra oficial.
Hay dos ramales, principal y final .
Encontré el código principal y el código final usando diferentes formas de navegar.
El código principal es simple y claro, y en otros proyectos, se basa en el estado de navegación al igual que el código A, que es del proyecto .
Code end use NavHostController
para navegar, pero parece que no necesitamos usar Navigation
nuevamente cuando usamos Jetpack Compose, ¿verdad?
código principal
@Composable fun RallyApp() { RallyTheme { val allScreens = RallyScreen.values().toList() var currentScreen by rememberSaveable { mutableStateOf(RallyScreen.Overview) } Scaffold( ... ) { innerPadding -> Box(Modifier.padding(innerPadding)) { currentScreen.content( onScreenChange = { screen -> currentScreen = RallyScreen.valueOf(screen) } ) } } } } enum class RallyScreen( val icon: ImageVector, val body: @Composable ((String) -> Unit) -> Unit ) { Overview( icon = Icons.Filled.PieChart, body = { OverviewBody() } ), Accounts( icon = Icons.Filled.AttachMoney, body = { AccountsBody(UserData.accounts) } ), Bills( icon = Icons.Filled.MoneyOff, body = { BillsBody(UserData.bills) } ); @Composable fun content(onScreenChange: (String) -> Unit) { body(onScreenChange) } }
Fin del código
@Composable fun RallyNavHost(navController: NavHostController, modifier: Modifier = Modifier) { NavHost( navController = navController, startDestination = Overview.name, modifier = modifier ) { composable(Overview.name) { OverviewBody( ... ) } composable(Accounts.name) { ... } composable(Bills.name) { ... } } } enum class RallyScreen( val icon: ImageVector, ) { Overview( icon = Icons.Filled.PieChart, ), Accounts( icon = Icons.Filled.AttachMoney, ), Bills( icon = Icons.Filled.MoneyOff, ); companion object { fun fromRoute(route: String?): RallyScreen = when (route?.substringBefore("/")) { Accounts.name -> Accounts Bills.name -> Bills Overview.name -> Overview null -> Overview else -> throw IllegalArgumentException("Route $route is not recognized.") } }
Código A
fun CraneHomeContent( ... ) { val suggestedDestinations by viewModel.suggestedDestinations.collectAsState() val onPeopleChanged: (Int) -> Unit = { viewModel.updatePeople(it) } var tabSelected by remember { mutableStateOf(CraneScreen.Fly) } BackdropScaffold( ... frontLayerContent = { when (tabSelected) { CraneScreen.Fly -> { ... } CraneScreen.Sleep -> { ... } CraneScreen.Eat -> { ... } } } ) }
He trabajado con Compose desde las primeras etapas alfa y rápidamente me decepcionó el pobre intento de Google de proporcionar un enfoque más moderno para navegar en una aplicación de una sola actividad. Cuando considera que el sistema basado en vistas de Android se reemplazó por completo con el enfoque de declaración que utiliza Compose, debe preguntarse seriamente por qué se quedarían con un controlador de navegación que no le permite pasar objetos de una pantalla a otra. También estaba el problema de que agregar animación al pasar de una pantalla a otra era una ocurrencia tardía. Hay un complemento que admite transiciones de animación.
Pero quizás lo peor de Compose fue su falta de manejo de los cambios de configuración del dispositivo. En el antiguo sistema basado en vistas, definía sus diseños en archivos xml y los colocaba en carpetas de recursos que tenían calificadores en el nombre de la carpeta que ayudarían a Android a elegir el diseño correcto en función de aspectos como la densidad de la pantalla, la orientación, el tamaño de la pantalla, etc. Eso se fue por la ventana con Compose. Eventualmente, Google agregó API para manejar componibles que deben seleccionarse según el tamaño y la densidad de la pantalla. Pero finalmente, terminas escribiendo esta lógica de decisión dentro de tu componible y tu código comienza a verse como espaguetis. El equipo de Android de Google se olvidó por completo de la "Separación de preocupaciones" más básica cuando eligieron mezclar diseños de interfaz de usuario con la lógica que determina qué diseño se selecciona. Diseñar tus componibles es una cosa y cómo se seleccionan es otra. No mezcle los dos. Sus componibles se vuelven cada vez menos reutilizables cuando integra esas API. Los desarrolladores originales de Android (que no eran de Google) sabían lo suficiente como para que el sistema operativo administrara la selección de diseño en función de los cambios en las configuraciones del dispositivo.
Elegí crear mi propio marco que maneje la navegación y administre componibles de una manera casi idéntica a como funciona el sistema basado en vistas. También soluciona el problema de no poder pasar objetos de una pantalla a otra. Si estás interesado, puedes consultarlo en:
https://github.com/JohannBlake/Jetmagic
Entonces, para responder a su pregunta sobre si Compose Navigation es bueno para navegar, tendría que decir que no. He trabajado con él y lo he encontrado gravemente deficiente. Si desea ser simplemente otro desarrollador de Android común y corriente, utilice Compose Navigation. Pero si desea trazar su propio camino y liberarse de las malas prácticas de diseño de Google, utilice Jetmagic o, mejor aún, cree su propio marco de navegación. No es tan difícil.
Para responder a su pregunta: Code Main
no es la forma correcta de navegar.
Este es un ejemplo para empezar. En ese caso, simplemente oculta y muestra Composables , eso es todo.
¿Por qué es mejor usar la navegación?
Lifecylce se está manejando mejor. Imagina que quieres iniciar una pantalla compatible con cameraX y luego quieres volver a tu pantalla compatible inicial. El componente de navegación manejará y liberará los recursos.
Los componibles se agregan a la pila trasera. Entonces, cuando presiona Atrás, automáticamente regresa a la pantalla anterior componible.
Obtiene esta bonita animación y no solo ve la siguiente pantalla al instante.
Estoy seguro de que también hay otros puntos, pero esos son algunos que me vinieron a la mente en este momento....
Depende del caso de uso específico que desee implementar. En general, sí, cualquier tipo de biblioteca de navegación es más adecuada para la navegación. Está literalmente en el nombre. La mayoría de las bibliotecas de navegación, incluido el Componente de navegación, proporcionan elementos esenciales como:
almacenar todo el historial de navegación (backstack) y el estado guardado ( rememberSaveable
) de cada entrada en backstack. El mismo destino puede estar varias veces en el backstack, pero se almacenarían como diferentes entradas del backstack. Esto permite almacenar los estados guardados por separado para cada uno de ellos.
proporcionando Lifecycle, ViewModelStore y SavedStateRegistry de ámbito independiente para cada uno de ellos. Esto puede ser útil para asignar/limpiar recursos mientras el destino está visible o mientras está en el backstack.
Hacer el cambio simple entre destinos como en su Code main
o Code A
PUEDE ser una opción válida, pero carece de la funcionalidad mencionada anteriormente. Todos sus destinos usan el mismo Lifecycle, ViewModelStore y SavedStateRegistry del componible principal. Además, rememberSaveable
no guardaría el estado al cambiar entre destinos. En algunos casos puede estar bien, pero debes ser consciente de la diferencia.
Sobre la pregunta de si el componente de navegación para Compose es bueno para la navegación. Está bien, pero hay fallas y complicaciones. Puede leer un buen artículo de CommonsWare sobre el tema: Navegación en Compose: Criterios
Teniendo en cuenta todas las rarezas de la biblioteca oficial, incluso he creado mi propia biblioteca de navegación: Compose Navigation Reimagined