La navegación describe la forma en que los usuarios se mueven por tu app. Los usuarios interactúan con los elementos de la IU, por lo general, presionando o haciendo clic en ellos, y la app responde mostrando contenido nuevo. Si el usuario quiere volver al contenido anterior, usa el gesto atrás o presiona el botón Atrás.
Cómo modelar el estado de navegación
Una forma conveniente de modelar este comportamiento es con una pila de contenido. A medida que el usuario navega hacia adelante para ver contenido nuevo, este se envía a la parte superior de la pila. Cuando regresan de ese contenido, este se quita de la pila y se muestra el contenido anterior. En términos de navegación, esta pila suele denominarse pila de actividades porque representa el contenido al que el usuario puede volver.
Crea una pila de actividades
En Navigation 3, la pila de actividades no contiene contenido. En cambio, contiene referencias al contenido, conocidas como claves. Las claves pueden ser de cualquier tipo, pero, por lo general, son clases de datos simples y serializables. Usar referencias en lugar de contenido tiene los siguientes beneficios:
- Es fácil de navegar, ya que se envían claves a la pila de actividades.
- Siempre que las claves se puedan serializar, la pila de actividades se puede guardar en el almacenamiento persistente, lo que le permite sobrevivir a los cambios de configuración y al cierre del proceso. Esto es importante porque los usuarios esperan salir de tu app, volver a ella más tarde y retomar lo que estaban haciendo con el mismo contenido que se mostraba. Consulta Cómo guardar la pila de actividades para obtener más información.
Un concepto clave en la API de Navigation 3 es que eres propietario de la pila de actividades. La biblioteca:
- Espera que tu pila de actividades sea un
List<T>respaldado por el estado de la instantánea, en el queTes el tipo de tu pila de actividadeskeys. Puedes usarAnyo proporcionar tus propias claves con tipos más estrictos. Cuando veas los términos "push" o "pop", la implementación subyacente consiste en agregar o quitar elementos del final de una lista. - Observa tu pila de actividades y refleja su estado en la IU con un
NavDisplay.
En el siguiente ejemplo, se muestra cómo crear claves y una pila de actividades, y modificar la pila de actividades en respuesta a los eventos de navegación del usuario:
// Define keys that will identify content data object ProductList data class ProductDetail(val id: String) @Composable fun MyApp() { // Create a back stack, specifying the key the app should start with val backStack = remember { mutableStateListOf<Any>(ProductList) } // Supply your back stack to a NavDisplay so it can reflect changes in the UI // ...more on this below... // Push a key onto the back stack (navigate forward), the navigation library will reflect the change in state backStack.add(ProductDetail(id = "ABC")) // Pop a key off the back stack (navigate back), the navigation library will reflect the change in state backStack.removeLastOrNull() }
Cómo resolver claves para el contenido
El contenido se modela en Navigation 3 con NavEntry, que es una clase que contiene una función de componibilidad. Representa un destino, un solo fragmento de contenido al que el usuario puede navegar hacia adelante y hacia atrás.
Un NavEntry también puede contener metadatos, que son información sobre el contenido. Los objetos de contenedor, como NavDisplay, pueden leer estos metadatos para ayudarlos a decidir cómo mostrar el contenido de NavEntry. Por ejemplo, los metadatos se pueden usar para anular las animaciones predeterminadas de un NavEntry específico. NavEntry
metadata es un mapa de claves String a valores Any, lo que proporciona un almacenamiento de datos
versátil.
Para convertir un key en un NavEntry, crea un proveedor de entradas. Esta es una función que acepta un key y devuelve un NavEntry para ese key. Por lo general, se define como un parámetro lambda cuando se crea un NavDisplay.
Existen dos maneras de crear un proveedor de entradas: creando una función lambda directamente o usando el DSL entryProvider.
Crea una función de proveedor de entrada directamente
Por lo general, creas una función de proveedor de entradas con una sentencia when, con
una rama para cada una de tus claves.
entryProvider = { key -> when (key) { is ProductList -> NavEntry(key) { Text("Product List") } is ProductDetail -> NavEntry( key, metadata = mapOf("extraDataKey" to "extraDataValue") ) { Text("Product ${key.id} ") } else -> { NavEntry(Unit) { Text(text = "Invalid Key: $it") } } } }
Usa el DSL de entryProvider
El DSL de entryProvider puede simplificar tu función lambda, ya que evita la necesidad de realizar pruebas con cada uno de tus tipos de claves y construir un NavEntry para cada uno.
Para ello, usa la función de compilador entryProvider. También incluye un comportamiento de resguardo predeterminado (arrojar un error) si no se encuentra la clave.
entryProvider = entryProvider { entry<ProductList> { Text("Product List") } entry<ProductDetail>( metadata = mapOf("extraDataKey" to "extraDataValue") ) { key -> Text("Product ${key.id} ") } }
Ten en cuenta lo siguiente sobre el fragmento:
entryse usa para definir unNavEntrycon el tipo y el contenido de componibilidad proporcionados.entryacepta un parámetrometadatapara establecerNavEntry.metadata
Muestra la pila de actividades
La pila de actividades representa el estado de navegación de tu app. Cada vez que cambia la pila de actividades, la IU de la app debe reflejar el nuevo estado de la pila de actividades. En Navigation 3, un NavDisplay observa tu pila de actividades y actualiza su IU según corresponda. Créalo con los siguientes parámetros:
- Tu pila de actividades, que debe ser del tipo
SnapshotStateList<T>, en el queTes el tipo de las claves de la pila de actividades Es unListobservable, por lo que activa la recomposición deNavDisplaycuando cambia. - Un
entryProviderpara convertir las claves de tu pila de actividades en objetosNavEntry - De manera opcional, proporciona una expresión lambda al parámetro
onBack. Se llama cuando el usuario activa un evento de retroceso.
En el siguiente ejemplo, se muestra cómo crear un NavDisplay.
data object Home data class Product(val id: String) @Composable fun NavExample() { val backStack = remember { mutableStateListOf<Any>(Home) } NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, entryProvider = { key -> when (key) { is Home -> NavEntry(key) { ContentGreen("Welcome to Nav3") { Button(onClick = { backStack.add(Product("123")) }) { Text("Click to navigate") } } } is Product -> NavEntry(key) { ContentBlue("Product ${key.id} ") } else -> NavEntry(Unit) { Text("Unknown route") } } } ) }
De forma predeterminada, el NavDisplay muestra el NavEntry superior en la pila de actividades en un diseño de panel único. En la siguiente grabación, se muestra esta app en ejecución:
NavDisplay: Comportamiento predeterminado con dos
destinos.Ciclo de vida del destino
NavDisplay usa LifecycleOwners personalizados para limitar el estado del ciclo de vida de un NavEntry en función de las restricciones a nivel de la escena y de la entrada.
Para obtener más información sobre los ciclos de vida en Compose, consulta Ciclo de vida en Jetpack Compose.
Restricciones del ciclo de vida a nivel de la escena
NavDisplay administra el ciclo de vida de los Scene activos. Los límites a nivel de escena se determinan de la siguiente manera:
Para escenas que no son de superposición, haz lo siguiente:
RESUMED: Permitido solo cuando la transición de escena se ha estabilizado y no hay escenas superpuestas activas mostrándose encima de ella.- _
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
STARTEDSTARTED
Para escenas superpuestas, como diálogos o hojas inferiores, haz lo siguiente:
RESUMED: Solo se permite para la escena de superposición más alta y activa en ese momento.STARTED: Se limita aSTARTEDpara cualquier escena de superposición subyacente que esté cubierta por una superposición más reciente.
Estado del ciclo de vida de nivel básico
La biblioteca administra el estado máximo del ciclo de vida de cada NavEntry individual en función de su presencia en la pila de actividades:
RESUMED: Si la entrada está presente en la pila de actividades actual, su ciclo de vida puede llegar hastaRESUMED(sujeto al límite de nivel de escena).CREATED: Si la entrada ya no está en la pila de actividades, como cuando se quitó, pero aún se renderiza en la pantalla mientras se anima, la biblioteca limita estrictamente su ciclo de vida aCREATED. Este límite garantiza que las entradas en segundo plano o de salida dejen de ejecutar el trabajo activo, como recopilar flujos o iniciar corrutinas vinculadas a los estadosRESUMEDoSTARTED, mientras finalizan sus transiciones de salida.
Cómo se combinan
Por ejemplo, el estado final del ciclo de vida de un NavEntry se resuelve de la siguiente manera:
| Situación | Límite a nivel de escena | Límite de nivel básico | Límite efectivo |
|---|---|---|---|
| Entrada activa, pantalla fija (sin transiciones ni superposiciones) | RESUMED |
RESUMED |
RESUMED |
| Entrada activa, durante la transición (navegación hacia o desde) | STARTED |
RESUMED |
STARTED |
| Entrada activa, cubierta por una superposición (por ejemplo, un diálogo está abierto) | STARTED |
RESUMED |
STARTED |
| Entrada emergente, animación de salida | STARTED o RESUMED |
CREATED |
CREATED |
Revisión general
En el siguiente diagrama, se muestra cómo fluyen los datos entre los distintos objetos en Navigation 3:
Los eventos de navegación inician los cambios. Las claves se agregan o quitan de la pila de actividades en respuesta a las interacciones del usuario.
El cambio en el estado de la pila de actividades activa la recuperación de contenido. El
NavDisplay(un elemento componible que renderiza una pila de actividades) observa la pila de actividades. En su configuración predeterminada, muestra la entrada de la pila de actividades superior en un diseño de panel único. Cuando cambia la clave superior de la pila de actividades, elNavDisplayusa esta clave para solicitar el contenido correspondiente al proveedor de entradas.El proveedor de entradas proporciona contenido. El proveedor de entradas es una función que resuelve una clave en un
NavEntry. Cuando recibe una clave deNavDisplay, el proveedor de entradas proporciona elNavEntryasociado, que contiene la clave y el contenido.Se muestra el contenido.
NavDisplayrecibeNavEntryy muestra el contenido.