Migrar para a Navigation 3

A navegação 3 representa uma mudança fundamental na forma como o Jetpack Compose processa o estado de navegação e oferece vantagens arquitetônicas significativas em relação à navegação 2.

Entenda as mudanças arquitetônicas e as etapas necessárias para migrar um app do Wear Compose da Navegação 2 para a Navegação 3.

Principais vantagens da Navigation 3

  • Controle direto da pilha de retorno: o NavBackStack é basicamente uma lista mutável de objetos NavKey, que representa o histórico de telas visitadas pelo usuário. Você controla exatamente como faria com qualquer MutableList do Kotlin (add, removeLast, clear). Manipule diretamente a lista para realizar ações de navegação, como adicionar uma chave para avançar ou remover uma chave para voltar.
  • Design "Compose-First": a backstack é modelada como um estado observável padrão. A modificação do histórico de navegação se comporta exatamente como a atualização de qualquer outro estado do Compose, acionando automaticamente a recomposição para mostrar a tela atual.
  • Segurança de tipo por padrão: as rotas baseadas em strings são eliminadas completamente. A navegação usa objetos de dados serializáveis e classes de dados.
  • Apresentações dissociadas (estratégias de cena): a camada de transição da interface (NavDisplay e SwipeDismissableSceneStrategy) é totalmente separada do rastreamento de estado (NavBackStack), permitindo uma integração mais simples das transições de navegação integradas do Wear OS.

Etapas da migração

1. Atualizar dependências

Remova a dependência androidx.wear.compose:compose-navigation antiga e introduza as novas dependências divididas do Navigation 3, além do suporte à serialização do Kotlin.

Remova:

implementation("androidx.wear.compose:compose-navigation:...")

Adicionar:

implementation("androidx.navigation3:navigation3-runtime:...") // State logic
implementation("androidx.navigation3:navigation3-ui:...")      // Display logic
implementation("androidx.wear.compose:compose-navigation3:...") // Wear gestures
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:...") // Requires compiler plugin

2. Atualizar destinos para implementar NavKey

Na Navegação 2, talvez você tenha usado strings ou objetos genéricos para o roteamento. Na Navegação 3, você precisa implementar a interface de marcador NavKey e anotar todos os objetos de tela com @Serializable.

Por que isso é necessário? Para garantir que o backstack possa ser salvo e restaurado após o encerramento do processo, o navigation3-runtime subjacente depende do kotlinx-serialization para serializar o estado.

Antes (Navigation 2: rotas genéricas com segurança de tipos):

sealed class Nav2Screen {
    data object Landing : Nav2Screen()
    data object List : Nav2Screen()
}

Depois (Navigation 3 - NavKey + Serializable):

@Serializable
sealed interface MigrationScreen : NavKey {
    @Serializable
    data object Landing : MigrationScreen

    @Serializable
    data object List : MigrationScreen
}

3. Substitua a lógica de roteamento (NavController para NavBackStack)

Substitua seu NavController por um NavBackStack inicializado via rememberNavBackStack. Você também precisa instanciar o SwipeDismissableSceneStrategy especificamente para o Wear OS.

Antes (Navegação 2):

val navController = rememberSwipeDismissableNavController()

Depois (Navegação 3):

val backStack = rememberNavBackStack(MigrationScreen.Landing as NavKey)
val strategy = rememberSwipeDismissableSceneStrategy<NavKey>()

4. Substitua NavHost por NavDisplay e a DSL entryProvider.

O contêiner NavHost e a DSL do builder composable("route") { ... } interna são substituídos por NavDisplay e pela DSL { entry<Key> { ... } } do entryProvider.

Antes (Navegação 2):

SwipeDismissableNavHost(navController = navController, startDestination = "menu") {
    composable("menu") {
        GreetingScreen(
            onShowList = { navController.navigate("list") }
        )
    }
    composable("list") {
        ListScreen()
    }
}

Depois (Navegação 3):

NavDisplay(
    backStack = backStack,
    sceneStrategies = listOf(strategy),
    entryProvider = entryProvider {
        entry<MigrationScreen.Landing> {
            GreetingScreen(
                onShowList = { backStack.add(MigrationScreen.List) }
            )
        }
        entry<MigrationScreen.List> {
            ListScreen()
        }
    }
)