Navegación 3

Navigation 3 es una biblioteca de navegación diseñada desde cero para Jetpack Compose. En esta guía, se explica cómo implementar Navigation 3 en aplicaciones para Wear OS.

Conceptos básicos

  • NavKey: Es un identificador serializable y con seguridad de tipos para un destino (pantalla) en tu app.
  • NavBackStack: Es una lista mutable de instancias de NavKey que representan el historial de navegación. Puedes enviar y mostrar elementos directamente desde esta lista.
  • rememberNavBackStack: Elemento componible que crea y conserva la pila de actividades en los cambios de configuración y el cierre del proceso.
  • NavDisplay: Es el componente principal de la IU que observa la pila de actividades y renderiza la pantalla activa.
  • EntryProvider: Es un DSL de asignación que vincula un NavKey a su IU de @Composable real.
  • SwipeDismissableSceneStrategy: Es la estrategia específica para Wear que une tus pantallas en un gesto de deslizar para descartar y controla las animaciones de atrás integradas.

Paso 1: Agrega dependencias

Agrega las dependencias necesarias de Navigation 3, Wear Compose y Serialization a tu proyecto.

Groovy

dependencies {
    // Core Navigation 3 APIs
    implementation "androidx.navigation3:navigation3-runtime:1.2.0-alpha02"
    implementation "androidx.navigation3:navigation3-ui:1.2.0-alpha02"

    // Wear OS specific Navigation 3 integration
    implementation "androidx.wear.compose:compose-navigation3:1.6.1"

    // Kotlinx Serialization for type-safe routing
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0"
}

Kotlin

dependencies {
    // Core Navigation 3 APIs
    implementation("androidx.navigation3:navigation3-runtime:1.2.0-alpha02")
    implementation("androidx.navigation3:navigation3-ui:1.2.0-alpha02")

    // Wear OS specific Navigation 3 integration
    implementation("androidx.wear.compose:compose-navigation3:1.6.1")

    // Kotlinx Serialization for type-safe routing
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0")
}

Paso 2: Define los destinos (NavKeys)

Las pantallas se definen como objetos serializables o clases de datos con escritura segura que implementan la interfaz NavKey.

@Serializable
sealed interface Screen : NavKey {
    @Serializable
    data object Home : Screen

    @Serializable
    data class Details(val itemId: String) : Screen
}

Paso 3: Configura NavDisplay y la pila de actividades

En la raíz de tu aplicación, inicializa la pila de actividades y la estrategia de escena de Wear OS, y, luego, conéctalas a NavDisplay.

// 1. Create the persistent back stack starting at the Home screen
val backStack = rememberNavBackStack(Screen.Home)

// 2. Initialize the Wear OS swipe-to-dismiss strategy
val strategy = rememberSwipeDismissableSceneStrategy<NavKey>()

// 3. Render the NavDisplay
NavDisplay(
    backStack = backStack,
    sceneStrategies = listOf(strategy),
    entryProvider = entryProvider {
        // 4. Map keys to Composables
        entry<Screen.Home> {
            HomeScreen(
                onNavigateToDetails = { id -> backStack.add(Screen.Details(id)) }
            )
        }
        entry<Screen.Details> { key ->
            DetailsScreen(
                itemId = key.itemId,
                onBack = { backStack.removeAt(backStack.lastIndex) }
            )
        }
    }
)

Paso 4: Realiza acciones de navegación

Dado que la pila de actividades secundarias es solo un MutableList personalizado, la navegación es increíblemente sencilla. Realizas operaciones directamente en la instancia de backStack:

  • Navegar hacia adelante: backStack.add(Screen.Details("123"))
  • Volver: backStack.removeLast() o backStack.removeLastOrNull()
  • Borrar y restablecer: backStack.clear(); backStack.add(Screen.Home) (o usa operaciones de lista para reemplazar la pila).

Paso 5: (Opcional) Define el alcance de los ViewModels para los destinos

De forma predeterminada, los ViewModel se limitan al Activity. Navigation 3 proporciona un artefacto específico (lifecycle-viewmodel-navigation3) para definir de forma segura el alcance de un ViewModel en un NavEntry en la pila de actividades. Cuando el destino se quita de la pila de actividades, se borra el ViewModel.

  1. Agrega la dependencia:

    implementation("androidx.lifecycle:lifecycle-viewmodel-navigation3:...")
    
  2. Agrega el decorador de almacén de ViewModel al entryDecorators de tu NavDisplay. También debes incluir de forma explícita SaveableStateHolderNavEntryDecorator cuando proporciones decoradores personalizados para conservar el estado de rememberSaveable de Compose:

    NavDisplay(
        backStack = backStack,
        sceneStrategies = listOf(strategy),
        entryDecorators = listOf(
            rememberSaveableStateHolderNavEntryDecorator(),
            rememberViewModelStoreNavEntryDecorator()
        ),
        entryProvider = entryProvider {
            entry<Screen.Home> {
                // Any viewModel() requested here will be scoped to this NavEntry
                val viewModel: HomeViewModel = viewModel()
            }
        }
    )