NavController — суштина, методе и управљање навигацијом у Jetpack Compose

Аутор: IT Sectr Објављено: 2026-06-29 Време читања: 7 мин

NavController је централна компонента библиотеке Navigation Compose, која управља стеком навигације и стањем back stack у Android применама. Путем NavController-а врше се преласи између екрана, повраћај на претходне странице и преношење података између рута. Према Android Developers (2025), NavController је обавезни елемент сваке Compose примене са више од једног екрана. Контролер се креира путем rememberNavController(), преноси се у NavHost и доступан је за позив navigate() са било које тачке композиције. Уграђена подршка за SavedStateHandle аутоматски чува стање ViewModel-а при реконфигурацији.

Главно

  • NavController — централни контролер навигације Compose, који управља back stack-ом и преласима између екрана
  • navigate() — основна метода за прелазак на руту са подршком NavOptions за управљање стеком
  • popBackStack() — повратак на претходни екран са опционалним брисањем до наведене руте
  • SavedStateHandle — интеграција са ViewModel за чување стања екрана при навигацији
  • currentBackStackEntryAsState() — праћење тренутне руте за синхронизацију UI

Шта је NavController у Jetpack Compose?

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.

kotlin
@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.

kotlin
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: управљање повратком и брисањем стека

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: чување стања екрана

SavedStateHandle — је механизам за чување стања ViewModel-а при навигацији и реконфигурацији. NavController аутоматски обезбеђује SavedStateHandle за сваки NavBackStackEntry. Кроз SavedStateHandle, ViewModel чува стање екрана и га враћа при повратку (restoreState = true).

У Navigation Compose, SavedStateHandle се користи заједно са ViewModel: ViewModel се иницијализује кроз SavedStateHandle који се преноси из backStackEntry. При преласку на други екран и повратку (са restoreState), ViewModel добија сачувано стање, а не креира се из почетка. Ово је критично за екране са уносом података, филтерима или скроловањем.

kotlin
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

currentBackStackEntryAsState() — функција која враћа State<NavBackStackEntry?>, који се ажурира при свакој промени тренутне руте. Ово је главни механизам синхронизације UI-а са навигацијом: BottomNavigation истиче активни елеменат, Toolbar ажурира наслов, Drawer се затвара при преласку.

Функција ради кроз snapshotFlow и collectAsState: при промени back stack-а, Compose поново компонује претплаћене елементе. Важно: currentBackStackEntryAsState() се ажурира тек након завршетка анимације преласка. За тренутно ажурирање користите currentDestination, који се мења синхроно са navigate(), али не подржава стање.

kotlin
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-а у једном Activity?

Технички да, али се не препоручује. Један NavController осигура доследан back stack и поједностављује отклањање. Више контролера су оправдани само за угнијеждене графове са одвојеном навигацијом (нпр. modal bottom sheet са сопственим стеком).

Како пренети NavController кроз ViewModel?

Преносите NavController у ViewModel кроз конструктор или DI. Међутим, боље је преносити само callback функције (onNavigate, onBack), а не сам NavController — ово поједностављује тестирање. За догађаје користите Channel<NavEvent> у ViewModel-у и сакупљајте у UI.

Зашто navigate не ради након асинхроне операције?

Проблем је у животном циклусу: ако NavController још није иницијализован (NavHost није изграђен), navigate() се игнорише. Користите LaunchedEffect за позив навигације након учитавања података, а не унутар корутине са произвољним lifecycle-ом.

Како очистити цео back stack и прећи на нови екран?

Позовите navController.navigate("target") { popUpTo(0) { inclusive = true } }. Параметар popUpTo(0) потпуно чисти стек, inclusive = true уклања и почетни унос. Заставица launchSingleTop = true спречава дуплирање нове руте.

По чему се разликује NavHostController од NavController?

NavHostController — наследник NavController-а са додатним методама за NavHost (нпр. setOnBackStackChangedListener). NavController — базна класа која се може користити ван NavHost-а за програмско управљање стеком. У већини случајева користи се NavHostController.

Резиме

  • NavController — централна компонента Navigation Compose, која управља стеком рута и преласима између екрана
  • navigate() врши прелазак са поставкама popUpTo, launchSingleTop и restoreState кроз NavOptions
  • popBackStack() управља повратком: један корак или масовно брисање до наведене руте са inclusive
  • SavedStateHandle се интегрише са ViewModel за аутоматско чување стања екрана при навигацији
  • currentBackStackEntryAsState() пружа реактивно праћење тренутне руте за синхронизацију UI
  • BackHandler обрађује системско дугме Back, а PredictiveBackGesture се подржава од NavController 2.9.0
  • За тестирање користите TestNavHostController са compose-test-rule и Semantics матчерима

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође