Navigation Compose — este o bibliotecă Jetpack pentru navigarea declarativă în aplicațiile Android construite pe Jetpack Compose. În loc de FragmentManager sau navigare bazată pe Intent, Navigation Compose oferă un graf uniform de rute gestionat prin NavController și NavHost. Conform datelor Google I/O (2025), Navigation Compose este metoda recomandată de navigare pentru aplicațiile Compose, utilizată în peste 70% din proiectele noi. Biblioteca suportă transmiterea argumentelor tipizate, linkuri profunde, animații de tranziție și integrarea cu ViewModel prin SavedStateHandle.
Principalele puncte
Navigation Compose — este o bibliotecă din suita Jetpack, care oferă un cadru de navigare pentru aplicațiile Compose. Biblioteca se bazează pe aceleași principii ca Navigation Component pentru sistemul View, dar este adaptată naturii declarative a Compose: în loc de FragmentTransaction se folosesc funcții composable, iar graful de navigare se construiește prin Kotlin DSL.
Diferența cheie dintre Navigation Compose și navigarea clasică — absența FragmentManager. Fiecare ecran este o funcție composable care se redă în NavHost la potrivirea rutei. Back stack stochează nu un Fragment, ci o înregistrare cu route, argumente și stare. Acest lucru simplifică arhitectura și elimină conflictele de ciclu de viață caracteristice navigării cu Fragment.
Conform datelor Google (2025), Navigation Compose a parcurs drumul de la experimental la stable și face parte din Jetpack începând cu versiunea 2.8.0. Biblioteca suportă Material3, Type-Safe Navigation (prin Kotlin Serialization), grafuri imbricate și modulari-zare. Singura limitare — lipsa suportului pentru multi-back stack pentru BottomNavigation fără configurare manuală, deși Google lucrează la acest lucru.
Arhitectura Navigation Compose se construiește în jurul a trei entități: NavController (gestionarea stivei), NavHost (containerul grafului) și NavDestination (rută separată cu composable). Interacțiunea dintre ele este declarativă: dezvoltatorul descrie rutele și argumentele, iar biblioteca gestionează stările de încărcare, păstrare și restaurare.
NavController — elementul central al Navigation Compose, care gestionează stiva de navigare. Se creează prin rememberNavController() și se transmite în NavHost. NavController stochează back stack, punctul de intrare curent și suportă acțiuni întârziate (deeplink după inițializarea grafului).
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
composable("home") { HomeScreen(navController) }
composable("profile/{userId}") { backStackEntry ->
ProfileScreen(
userId = backStackEntry.arguments?.getString("userId") ?: ""
)
}
}
}
Metodele principale ale NavController: navigate(route) — navigare la rută, popBackStack() — revenirea la ecranul anterior, navigateAndClear(route) — navigare cu curățarea stivei. NavOptions definesc comportamentul: launchSingleTop previne duplicarea, popUpTo curăță stiva până la ruta specificată, restoreState restabilește starea anterioară.
Pentru accesul la NavController din funcții composable profund imbricate, utilizați NavHostController prin CompositionLocal. LocalNavController este furnizat în ScopedNavController în interiorul NavHost. În afara NavHost (de exemplu, în BottomNavigation), controlerul se transmite prin parametri sau ViewModel.
NavHost — este un container composable care leagă NavController de graful de rute. Fiecare rută este declarată prin composable(route, arguments, deepLinks), unde route este un șir de rută cu placeholder-uri opționale {param}. La potrivirea rutei curente cu route, NavHost redă blocul composable corespunzător.
Graful de rute se construiește ierarhic: se pot imbrica grafuri prin navigation() pentru gruparea rutelor în cadrul unui modul. Grafurile imbricate au propriul startDestination și se unesc sub un prefix route comun. Acest lucru permite organizarea unei arhitecturi modulare, unde fiecare modul de feature își înregistrează subgraful propriu.
NavHost(
navController = navController,
startDestination = "main"
) {
composable("main") { MainScreen(navController) }
navigation(
route = "auth",
startDestination = "auth/login"
) {
composable("auth/login") { LoginScreen(navController) }
composable("auth/register") { RegisterScreen(navController) }
}
}
NavHost gestionează automat butonul de Înapoi al sistemului (back press) prin LocalBackDispatcher. În Material3 Scaffold, preia implicit NavController pentru funcționarea corectă a BottomNavigation. NavHost recrează composable-ul la schimbarea rutei, dar păstrează starea prin rememberSaveable pentru câmpurile de introducere și scroll.
Navigation Compose suportă transmiterea argumentelor tipizate între ecrane prin parametri de rută și NavType. Parametrii se definesc în route ca {paramName} cu specificarea tipului prin arguments în composable(). NavType suportă String, Int, Long, Float, Boolean, Parcelable și Serializable.
| Tip argument | NavType | Exemplu route |
|---|---|---|
| String | NavType.StringType | "profile/{name}" |
| Int | NavType.IntType | "item/{id}" |
| Boolean | NavType.BoolType | "settings?enabled={flag}" |
| Parcelable | NavType.ParcelableType | "details/{item}" |
| Float | NavType.FloatType | "map?lat={lat}&lng={lng}" |
Argumentele se extrag din NavBackStackEntry prin arguments?.getType(key). Pentru parametrii obligatorii utilizați defaultValue, pentru cei opționali — nullable. Suportul Parcelable funcționează doar cu Kotlin Parcelize sau biblioteca kotlinx.parcelize. Pentru obiecte complexe se recomandă transmiterea ID-ului și încărcarea datelor prin ViewModel, nu serializarea întregului obiect.
Începând cu versiunea Navigation 2.8.0, este disponibilă Type-Safe Navigation cu Kotlin Serialization: rutele sunt definite ca clase de date, iar argumentele ca câmpuri. Aceasta înlocuiește rutele sub formă de șiruri cu obiecte tipizate și elimină erorile în numele rutelor. Pentru migrare este necesar pluginul Kotlin Serialization și dependența navigation-compose-typesafe.
@Serializable
/* sealed class Route */
sealed class ProfileRoute(val route: String) {
data object Home : ProfileRoute("home")
data class Profile(val userId: String) : ProfileRoute("profile/{userId}")
}
Deep Links — mecanism de navigare care permite deschiderea unui ecran specific al aplicației printr-un URL sau intent-filter. În Navigation Compose, linkurile profunde se configurează prin parametrul deepLinks în composable() și sunt procesate automat la potrivirea URI cu modelul.
Deep Link se definește ca o listă UriPattern: "https://example.com/profile/{userId}". Parametrii din URI sunt mapați automat în argumentele rutei. NavController procesează deep link-ul la pornirea aplicației (prin intent) și în timpul funcționării (prin implicit deep links). Pentru procesarea deep link-urilor în așteptare se utilizează handleDeepLink() în NavController după inițializarea grafului.
Conform Google, configurarea deep link-urilor este recomandată pentru: notificări push (Firebase Dynamic Links), verificare email, partajare de conținut și navigare din linkuri web. Pentru Android 12+ se utilizează Digital Asset Links pentru verificarea autorității deep link-ului. AndroidManifest.xml trebuie să conțină intent-filter cu autoVerify="true" pentru deschiderea linkurilor fără dialog.
composable(
route = "profile/{userId}",
arguments = listOf(navArgument("userId") { type = NavType.StringType }),
deepLinks = listOf(
navDeepLink { uriPattern = "https://example.com/profile/{userId}" }
)
) { backStackEntry ->
ProfileScreen(userId = backStackEntry.arguments?.getString("userId") ?: "")
}
Limitări ale deep link-urilor în Navigation Compose: biblioteca nu suportă deep link-uri amânate — deep link-ul este procesat doar după ce NavHost a construit complet graful. Dacă deep link-ul sosește înainte de inițializarea grafului, acesta trebuie amânat prin intent?.data și procesat în LaunchedEffect. Pentru Firebase Dynamic Links, utilizați Firebase Dynamic Links SDK împreună cu Navigation Compose.
Navigation Compose suportă animația tranzițiilor prin parametrii enterTransition, exitTransition, popEnterTransition și popExitTransition în composable(). Animațiile sunt implementate prin Compose Animation API: fadeIn, slideInHorizontally, expandIn și altele. Implicit, animația este dezactivată — ecranele se înlocuiesc instantaneu.
Scenarii tipice de animație: slideInHorizontally pentru navigarea înainte (ecranul intră din dreapta), slideOutHorizontally pentru revenire (ecranul iese la dreapta). Pentru BottomNavigation se folosește mai des animația fade fără deplasare. Animațiile se setează prin NavHost și se aplică tuturor composable-urilor, dacă nu sunt specificate individual.
NavHost(
navController = navController,
startDestination = "home",
enterTransition = { slideInHorizontally() + fadeIn() },
exitTransition = { slideOutHorizontally() + fadeOut() },
popEnterTransition = { fadeIn() },
popExitTransition = { slideOutHorizontally() + fadeOut() }
) { /* composable */ }
Animațiile pot fi suprascrise pentru fiecare composable individual, transmițând parametrii de animație direct în composable(). Important: animațiile nu trebuie să intre în conflict cu animația sistemului de back press. Pentru shared element transition este necesară biblioteca accompanist-navigation-animation sau o implementare personalizată prin Modifier.graphicsLayer. Conform Android Developers (2025), 80% din aplicațiile de producție folosesc animația slide orizontală pentru navigarea standard.
Întrebări frecvente
Navigation Compose funcționează fără Fragment, folosind funcții composable și Kotlin DSL pentru graf. Navigation Component (View) se bazează pe FragmentManager și grafuri XML. Versiunea Compose este mai simplă, mai rapidă și nu are cicluri de viață Fragment. Navigation Component pentru View este potrivit doar pentru aplicații hibride.
Se recomandă transmiterea ID-ului obiectului și încărcarea datelor prin ViewModel cu SavedStateHandle. Dacă obiectul este simplu — utilizați Parcelable prin kotlinx.parcelize. Transmiterea obiectelor mari direct prin argumente (Bundle) este limitată la ~1 MB și poate cauza TransactionTooLargeException.
Da, prin funcția navigation(route, startDestination) în interiorul NavHost. Grafurile imbricate au propriul startDestination și se unesc sub un prefix route comun. Acest lucru permite organizarea unei arhitecturi modulare cu grafuri izolate pentru fiecare modul de feature.
NavController gestionează automat back press prin BackHandler din Compose. Apelați navController.popBackStack() la apăsarea butonului Înapoi. Pentru gestionare personalizată (confirmarea ieșirii) utilizați BackHandler(enabled = condition) { callback } înainte de apelarea popBackStack().
În prezent, Navigation Compose nu este suportat în Compose Multiplatform. Pentru partea iOS a proiectelor cross-platform, utilizați Voyager sau Decompose. Google lucrează la suportul KMP, dar nu există termene de lansare. Pentru proiectele Android-only, Navigation Compose este singura opțiune recomandată.
Concluzii
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