NavController је централна компонента библиотеке Navigation Compose, која управља стеком навигације и стањем back stack у Android применама. Путем NavController-а врше се преласи између екрана, повраћај на претходне странице и преношење података између рута. Према Android Developers (2025), NavController је обавезни елемент сваке Compose примене са више од једног екрана. Контролер се креира путем rememberNavController(), преноси се у NavHost и доступан је за позив navigate() са било које тачке композиције. Уграђена подршка за SavedStateHandle аутоматски чува стање ViewModel-а при реконфигурацији.
Главно
NavController — је класа из библиотеке Navigation Compose која имплементира контролер навигације за Compose примене. NavController управља стеком NavBackStackEntry, где сваки унос садржи руту, аргументе и стање екрана. Контролер подржава основне операције навигације: прелазак, повратак, замену и брисање.
За разлику од View система, где се навигација одвијала кроз FragmentManager или Intent, NavController ради искључиво у Compose контексту. Back stack се чува у облику NavDestination графа, а не Fragment стека. Ово елиминише overhead за креирање и уништавање Fragment-а, као и поједностављује тестирање — NavController се може моковати кроз TestNavHostController.
NavController је чврсто повезан са NavHost — контејнером који рендерује тренутни екран из графа. Без NavHost-а, NavController не може да прикаже composable функције, али задржава могућност управљања стеком. У типичној архитектури, NavController се креира на нивоу Activity или главног composable-а и преноси се надоле кроз дрво композиције путем параметара.
Према Google-у, NavController је прошао кроз неколико major издања. Версија 2.8.0 је додала Type-Safe Navigation, версија 2.9.0 — подршку за predictive back gesture (Android 14+). Контролер је компатибилан са Material3 Scaffold и BottomNavigation. За мултимодулне пројекте, NavController се преноси кроз DI (Hilt/Koin) или параметре конструктора.
NavController се креира путем composable функције rememberNavController(). Функција враћа инстанцу NavHostController (наследник NavController), повезану са животним циклусом тренутног composable-а. Напуштањем композиције, контролер се чисти. За чување контролера при реконфигурацији користите rememberSaveable или ViewModel.
@Composable
fun MyApp() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "main"
) {
composable("main") { MainScreen(navController) }
composable("details") { DetailsScreen(navController) }
}
}
Конфигурација NavController-а укључује: NavHostController (главни), TestNavHostController (тестирање) и ScopedNavController (подређени за угнијеждене графове). За BottomNavigation, NavController треба да буде јединствен за целу апликацију — креирање новог контролера у свакој картици довешче до губитка стека. За преношење контролера у угнијеждене екране користите параметар функције, а не CompositionLocalProvider, да би се сачувала читљивост.
За тестирање навигације користите TestNavHostController са compose-test-rule. Контролер омогућава постављање почетне руте и проверу да је navigate() позвао очекивани прелазак. Тестирање NavController-а не захтева емулатор — ради са Semantics матчерима Compose Test.
Метода navigate(route: String) — основни начин навигације у NavController-у. Прима стринг руте, опционалне NavOptions и Navigator.Extras. NavOptions управљају понашањем преласка: launchSingleTop (не дуплирај руту у стеку), popUpTo (очисти стек до руте), restoreState (врати претходно стање).
NavOptions се постављају кроз builder синтаксу: NavOptionsBuilder. Главни параметри: popUpTo (route + inclusive/saveState), launchSingleTop (Boolean, true — не прави дупликат), restoreState (врати стање при повратку). Без popUpTo, сваки navigate() додаје унос у стек, што доводи до нагомилавања back stack-а и неисправног рада дугмета Back.
navController.navigate("profile/42") {
popUpTo("main") { saveState = true }
launchSingleTop = true
restoreState = true
}
Navigator.Extras омогућава преношење додатних података који нису део руте: shared element за анимацију, Intent заставице, Pac-Man bundle. Extras се ретко користе — углавном за интеграцију са Accompanist Animation или прилагођеним Navigator-ом. За већину сценарија, довољни су стринг руте и NavOptions.
popBackStack() — метода за повратак на претходни екран. Без аргумената уклања горњи унос стека и враћа true ако је уклањање успешно. Ако је стек празан — метода враћа false, а Activity се затвара (аналогно super.onBackPressed()).
Преоптерећена версија popBackStack(route: String, inclusive: Boolean) уклања све уносе до наведене руте. Ако је inclusive = true — уклања се и сама наведена рута. Метода враћа Boolean — true ако су уноси пронађени и избрисани. Версија са inclusive је корисна за сценарије „излазак на корени екран” након ауторизације или поруцибе.
| Метода | Опис | Пример |
|---|---|---|
| popBackStack() | Повратак један екран уназад | navController.popBackStack() |
| popBackStack(route, false) | Брисање до руте (рута остаје) | popBackStack("home", false) |
| popBackStack(route, true) | Брисање до и укључујући руту | popBackStack("home", true) |
| navigate(route) { popUpTo(route) { inclusive = true } } | Прелазак са потпуним брисањем | navigate("login") { popUpTo(0) { inclusive = true } } |
За обраду системског дугмета Back (hardware back button) користите BackHandler из Compose-а. BackHandler прима enabled и onBack — callback који се позива при притиску. За Android 14+ користи се PredictiveBackGesture, интегриран кроз NavController од версије 2.9.0. Predictive back додаје анимацију прегледа повратка.
SavedStateHandle — је механизам за чување стања ViewModel-а при навигацији и реконфигурацији. NavController аутоматски обезбеђује SavedStateHandle за сваки NavBackStackEntry. Кроз SavedStateHandle, ViewModel чува стање екрана и га враћа при повратку (restoreState = true).
У Navigation Compose, SavedStateHandle се користи заједно са ViewModel: ViewModel се иницијализује кроз SavedStateHandle који се преноси из backStackEntry. При преласку на други екран и повратку (са restoreState), ViewModel добија сачувано стање, а не креира се из почетка. Ово је критично за екране са уносом података, филтерима или скроловањем.
class ProfileViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
val userId: String = savedStateHandle.get<String>("userId") ?: ""
var searchQuery by savedStateHandle.getStateFlow("search", "")
.collectAsState()
}
SavedStateHandle подржава примитивне типове, String, Bundle и Parcelable. За сложене објекте, чувајте само ID, а пуне податке учитајте из репозиторијума. Лимит SavedStateHandle-а је око 1 MB, прекорачење изазива TransactionTooLargeException. За велике количине користите Room или DataStore уместо чувања у handle.
Важно: SavedStateHandle чува стање само када се користи restoreState = true у NavOptions. Ако restoreState није наведен, при повратку ViewModel се креира из нова са подразумевајућим вредностима. За пребацивање BottomNavigation са restoreState, NavController чува стање сваке картице и га враћа при поновном избору.
currentBackStackEntryAsState() — функција која враћа State<NavBackStackEntry?>, који се ажурира при свакој промени тренутне руте. Ово је главни механизам синхронизације UI-а са навигацијом: BottomNavigation истиче активни елеменат, Toolbar ажурира наслов, Drawer се затвара при преласку.
Функција ради кроз snapshotFlow и collectAsState: при промени back stack-а, Compose поново компонује претплаћене елементе. Важно: currentBackStackEntryAsState() се ажурира тек након завршетка анимације преласка. За тренутно ажурирање користите currentDestination, који се мења синхроно са navigate(), али не подржава стање.
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
Text(
text = when (currentRoute) {
"home" -> "Home"
"profile" -> "Profile"
else -> ""
}
)
За приступ аргументима тренутне руте користите navBackStackEntry?.arguments. Ово је згодно у BottomNavigation: selectedItem се рачуна на основу currentRoute. За отклањање навигације користите NavController.addOnDestinationChangedListener(), који логује сваки прелазак. У production-у, избегавајте претплата унутар великог броја composable-а — направите један извор у ViewModel-у и преносите State у UI.
Често постављана питања
Технички да, али се не препоручује. Један NavController осигура доследан back stack и поједностављује отклањање. Више контролера су оправдани само за угнијеждене графове са одвојеном навигацијом (нпр. modal bottom sheet са сопственим стеком).
Преносите NavController у ViewModel кроз конструктор или DI. Међутим, боље је преносити само callback функције (onNavigate, onBack), а не сам NavController — ово поједностављује тестирање. За догађаје користите Channel<NavEvent> у ViewModel-у и сакупљајте у UI.
Проблем је у животном циклусу: ако NavController још није иницијализован (NavHost није изграђен), navigate() се игнорише. Користите LaunchedEffect за позив навигације након учитавања података, а не унутар корутине са произвољним lifecycle-ом.
Позовите navController.navigate("target") { popUpTo(0) { inclusive = true } }. Параметар popUpTo(0) потпуно чисти стек, inclusive = true уклања и почетни унос. Заставица launchSingleTop = true спречава дуплирање нове руте.
NavHostController — наследник NavController-а са додатним методама за NavHost (нпр. setOnBackStackChangedListener). NavController — базна класа која се може користити ван NavHost-а за програмско управљање стеком. У већини случајева користи се NavHostController.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође