NavController este componenta centrală a bibliotecii Navigation Compose, care gestionează stiva de navigație și starea back stack în aplicațiile Android. Prin NavController se efectuează tranziții între ecrane, revenirea la paginile anterioare și transmiterea datelor între rute. Conform Android Developers (2025), NavController este un element obligatoriu al oricărei aplicații Compose cu mai mult de un ecran. Controlerul este creat prin rememberNavController(), transmis către NavHost și disponibil pentru apelarea navigate() din orice punct al compoziției. Suportul încorporat SavedStateHandle salvează automat starea ViewModel la reconfigurare.
Principalele
NavController — este o clasă din biblioteca Navigation Compose care implementează controlerul de navigație pentru aplicațiile Compose. NavController gestionează stiva NavBackStackEntry, unde fiecare intrare conține ruta, argumentele și starea ecranului. Controlerul suportă operații de bază de navigare: tranziție, revenire, înlocuire și curățare.
Spre deosebire de sistemul View, unde navigarea se făcea prin FragmentManager sau Intent, NavController funcționează exclusiv în contextul Compose. Back stack este stocat sub forma unui graf NavDestination, nu a unei stive de Fragment. Aceasta elimină overhead-ul de creare și distrugere a Fragment, precum și simplifică testarea — NavController poate fi mock-uit prin TestNavHostController.
NavController este strâns legat de NavHost — containerul care render-uiește ecranul curent din graf. Fără NavHost, NavController nu poate afișa funcții composable, dar păstrează capacitatea de a gestiona stiva. În arhitectura tipică, NavController este creat la nivelul Activity sau al composable-ului principal și transmis în jos pe arborele compoziției prin parametri.
Conform Google, NavController a trecut prin mai multe versiuni majore. Versiunea 2.8.0 a adăugat Type-Safe Navigation, versiunea 2.9.0 — suport pentru predictive back gesture (Android 14+). Controlerul este compatibil cu Material3 Scaffold și BottomNavigation. Pentru proiecte multimodulare, NavController se transmite prin DI (Hilt/Koin) sau parametri de constructor.
NavController se creează prin funcția composable rememberNavController(). Funcția returnează o instanță NavHostController (moștenitor al NavController), legată de ciclul de viață al composable-ului curent. La ieșirea din compoziție, controlerul este curățat. Pentru salvarea controlerului la reconfigurare, utilizați rememberSaveable sau ViewModel.
@Composable
fun MyApp() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "main"
) {
composable("main") { MainScreen(navController) }
composable("details") { DetailsScreen(navController) }
}
}
Configurarea NavController include: NavHostController (principal), TestNavHostController (testare) și ScopedNavController (copil pentru grafuri imbricate). Pentru BottomNavigation, NavController trebuie să fie unic pentru întreaga aplicație — crearea unui nou controler în fiecare filă va duce la pierderea stivei. Pentru transmiterea controlerului către ecranele imbricate, utilizați parametrul funcției, nu CompositionLocalProvider, pentru a păstra lizibilitatea.
Pentru testarea navigației, utilizați TestNavHostController cu compose-test-rule. Controlerul permite setarea rutei inițiale și verificarea că navigate() a apelat tranziția așteptată. Testarea NavController nu necesită emulator — funcționează cu matcher-ele Semantics din Compose Test.
Metoda navigate(route: String) — modul principal de navigare în NavController. Primește un șir de rută, NavOptions opționale și Navigator.Extras. NavOptions gestionează comportamentul tranziției: launchSingleTop (nu duplica ruta în stivă), popUpTo (curăță stiva până la rută), restoreState (restaurează starea anterioară).
NavOptions se setează prin sintaxa builder: NavOptionsBuilder. Parametrii principali: popUpTo (route + inclusive/saveState), launchSingleTop (Boolean, true — nu crea duplicat), restoreState (restaurează starea la revenire). Fără popUpTo, fiecare navigate() adaugă o intrare în stivă, ceea ce duce la acumularea back stack și comportamentul incorect al butonului Back.
navController.navigate("profile/42") {
popUpTo("main") { saveState = true }
launchSingleTop = true
restoreState = true
}
Navigator.Extras permite transmiterea de date suplimentare care nu fac parte din rută: shared element pentru animație, flag-uri Intent, Pac-Man bundle. Extras se utilizează rar — în principal pentru integrarea cu Accompanist Animation sau Navigator personalizat. Pentru majoritatea scenariilor, sunt suficiente șirul de rută și NavOptions.
popBackStack() — metoda de revenire la ecranul anterior. Fără argumente, șterge intrarea de sus a stivei și returnează true dacă ștergerea a reușit. Dacă stiva este goală — metoda returnează false, iar Activity se închide (similar cu super.onBackPressed()).
Versiunea supraîncărcată popBackStack(route: String, inclusive: Boolean) șterge toate intrările până la ruta specificată. Dacă inclusive = true — se șterge și ruta specificată. Metoda returnează Boolean — true dacă s-au găsit și șters intrări. Versiunea cu inclusive este utilă pentru scenarii de „ieșire la ecranul principal“ după autorizare sau finalizare comandă.
| Metodă | Descriere | Exemplu |
|---|---|---|
| popBackStack() | Revenire cu un ecran înapoi | navController.popBackStack() |
| popBackStack(route, false) | Curățare până la rută (ruta rămâne) | popBackStack("home", false) |
| popBackStack(route, true) | Curățare până la și inclusiv ruta | popBackStack("home", true) |
| navigate(route) { popUpTo(route) { inclusive = true } } | Tranziție cu curățare completă | navigate("login") { popUpTo(0) { inclusive = true } } |
Pentru gestionarea butonului Back de sistem (hardware back button), utilizați BackHandler din Compose. BackHandler primește enabled și onBack — callback apelat la apăsare. Pentru Android 14+ se utilizează PredictiveBackGesture, integrat prin NavController din versiunea 2.9.0. Predictive back adaugă o animație de previzualizare a revenirii.
SavedStateHandle — este un mecanism de salvare a stării ViewModel la navigare și reconfigurare. NavController furnizează automat SavedStateHandle pentru fiecare NavBackStackEntry. Prin SavedStateHandle, ViewModel stochează starea ecranului și o restaurează la revenire (restoreState = true).
În Navigation Compose, SavedStateHandle este utilizat împreună cu ViewModel: ViewModel este inițializat prin SavedStateHandle, care este transmis din backStackEntry. La trecerea la un alt ecran și revenire (cu restoreState), ViewModel primește starea salvată, nu este creat din nou. Acest lucru este critic pentru ecranele cu introducere de date, filtre sau derulare.
class ProfileViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
val userId: String = savedStateHandle.get<String>("userId") ?: ""
var searchQuery by savedStateHandle.getStateFlow("search", "")
.collectAsState()
}
SavedStateHandle suportă tipuri primitive, String, Bundle și Parcelable. Pentru obiecte complexe, salvați doar ID-ul, iar datele complete încărcați din depozit. Limita SavedStateHandle este de aproximativ 1 MB, depășirea cauzează TransactionTooLargeException. Pentru volume mari, utilizați Room sau DataStore în loc de salvare în handle.
Important: SavedStateHandle salvează starea doar la utilizarea restoreState = true în NavOptions. Dacă restoreState nu este specificat, la revenire ViewModel este creat din nou cu valori implicite. Pentru comutarea BottomNavigation cu restoreState, NavController salvează starea fiecărei file și o restaurează la selectarea repetată.
currentBackStackEntryAsState() — funcție care returnează State<NavBackStackEntry?>, care se actualizează la fiecare schimbare a rutei curente. Acesta este mecanismul principal de sincronizare a UI cu navigarea: BottomNavigation evidențiază elementul activ, Toolbar actualizează titlul, Drawer se închide la tranziție.
Funcția funcționează prin snapshotFlow și collectAsState: la schimbarea back stack, Compose recompune elementele abonate. Important: currentBackStackEntryAsState() se actualizează doar după finalizarea animației de tranziție. Pentru actualizare imediată, utilizați currentDestination, care se schimbă sincron cu navigate(), dar nu suportă starea.
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
Text(
text = when (currentRoute) {
"home" -> "Home"
"profile" -> "Profile"
else -> ""
}
)
Pentru accesul la argumentele rutei curente, utilizați navBackStackEntry?.arguments. Acest lucru este convenabil în BottomNavigation: selectedItem se calculează pe baza currentRoute. Pentru depanarea navigației, utilizați NavController.addOnDestinationChangedListener(), care loghează fiecare tranziție. În producție, evitați abonamentele în interiorul unui număr mare de composable — creați o sursă unică în ViewModel și transmiteți State în UI.
Întrebări frecvente
Tehnic da, dar nu este recomandat. Un singur NavController asigură un back stack coerent și simplifică depanarea. Controlere multiple sunt justificate doar pentru grafuri imbricate cu navigare separată (de exemplu, modal bottom sheet cu propria stivă).
Transmiteți NavController în ViewModel prin constructor sau DI. Cu toate acestea, este mai bine să transmiteți doar funcții callback (onNavigate, onBack), nu NavController însuși — aceasta simplifică testarea. Pentru evenimente, utilizați Channel<NavEvent> în ViewModel și colectați în UI.
Problema este în ciclul de viață: dacă NavController nu este încă inițializat (NavHost nu este construit), navigate() este ignorat. Utilizați LaunchedEffect pentru a apela navigarea după încărcarea datelor, nu în interiorul unei coroutine cu un lifecycle arbitrar.
Apelați navController.navigate("target") { popUpTo(0) { inclusive = true } }. Parametrul popUpTo(0) curăță complet stiva, inclusive = true șterge și intrarea inițială. Flag-ul launchSingleTop = true previne duplicarea noii rute.
NavHostController — moștenitor al NavController cu metode suplimentare pentru NavHost (de exemplu, setOnBackStackChangedListener). NavController — clasa de bază care poate fi utilizată în afara NavHost pentru gestionarea programatică a stivei. În majoritatea cazurilor, se utilizează NavHostController.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și