«Rota perdida»: por que o Android Navigation Component enlouquece
\n\nVocê já iniciou seu aplicativo no Android, navegou para uma tela, pressionou «Voltar» — e de repente o aplicativo travou com o erro IllegalArgumentException: navigation destination xxx is unknown to this NavController? Se sim — você não está sozinho. Esta questão acumulou milhares de visualizações no Stack Overflow, e um em cada dois desenvolvedores iniciando com Jetpack Navigation se depara com ela.
Neste artigo, vamos analisar por que ocorre o IllegalArgumentException navigation destination unknown, como diagnosticá-lo e, principalmente, como corrigi-lo. Teremos muito código vivo em Kotlin, dicas práticas e nenhum «enrolação». Vamos lá!
\n\nO que é NavController e por que ele «não conhece» seu destino?
\n\nPara começar — um pouco de teoria. NavController é o maestro principal da navegação no Android. Ele gerencia as transições entre telas (fragments ou composables) com base no grafo de navegação — um arquivo XML onde todas as rotas possíveis são descritas. Quando você chama navController.navigate(R.id.some_destination), o NavController procura este ID em seu grafo. Se não encontrar — lança um IllegalArgumentException.
Parece lógico, mas por que isso acontece em tempo de execução, e não durante a compilação? Porque o grafo de navegação é carregado dinamicamente, e o erro geralmente está relacionado ao fato de que o grafo atual (que o NavController contém) simplesmente não inclui o destino solicitado.
\n\nCenários mais comuns do erro
\n- \n
- Tentativa de navegar para uma tela que não está descrita no grafo atual — por exemplo, você alterna fragments, mas o NavController permaneceu do anterior. \n
- Uso de links profundos (DeepLink) sem registro adequado — o aplicativo é iniciado por um link, mas o grafo ainda não foi carregado. \n
- Limpeza da back stack e tentativa de navegação após isso — o NavController «esquece» suas rotas. \n
- ID de destino incorreto — erro de digitação ou incompatibilidade de nome no código e no XML. \n
Análise com um exemplo real (Kotlin)
\n\nImagine que você tem um aplicativo com duas telas: HomeFragment e ProfileFragment. O grafo de navegação nav_graph.xml se parece com isso:
<!-- nav_graph.xml -->\n<navigation xmlns:android="http://schemas.android.com/apk/res/android"\n xmlns:app="http://schemas.android.com/apk/res-auto"\n android:id="@+id/nav_graph"\n app:startDestination="@id/homeFragment">\n\n <fragment\n android:id="@+id/homeFragment"\n android:name="com.example.app.HomeFragment"\n android:label="Home">\n <action\n android:id="@+id/action_home_to_profile"\n app:destination="@id/profileFragment" />\n </fragment>\n\n <fragment\n android:id="@+id/profileFragment"\n android:name="com.example.app.ProfileFragment"\n android:label="Profile" />\n</navigation>\n\nNo HomeFragment, você faz a transição:
// HomeFragment.kt\nval navController = findNavController()\nnavController.navigate(R.id.action_home_to_profile)\n\nTudo funciona. Mas e se você tentar chamar navController.navigate(R.id.profileFragment) de outro contexto — por exemplo, de uma Activity ou BroadcastReceiver? Ou se você recarregou o grafo? É aí que aparece o IllegalArgumentException: navigation destination xxx is unknown to this NavController.
Reproduzindo o erro
\n\nSuponha que você adicionou um botão «Voltar» na toolbar e escreveu este código:
\n\n// MainActivity.kt\nsupportActionBar?.setDisplayHomeAsUpEnabled(true)\n\noverride fun onOptionsItemSelected(item: MenuItem): Boolean