composable() — је функција библиотеке Navigation Compose која региструје екран у NavHost-у и повезује URL путању са Compose распоредом. Када навигација пређе на задату путању, Jetpack Compose позива одговарајућу composable функцију и приказује је као тренутни екран. За разлику од FragmentManager-а или Intent-базиране навигације, composable() ради на нивоу једне Activity и потпуно се управља кроз Kotlin DSL. Према подацима Android Developers (2025), више од 73% модерних Android апликација изграђених на Jetpack Compose-у користи управо Navigation Compose за организовање промене екрана.
Главно
composable() — је функција-проширење (extension function) објекта NavHost. Kotlin DSL омогућава да се позива унутар NavHost блока за декларативно описивање свих екрана апликације. Сваки позив ствара унос у навигационом графу, повезујући текстуалну путању са composable функцијом. Када корисник пређе на одређену путању, NavHost приказује одговарајући composable као тренутни екран, сакривајући претходни.
Библиотека Navigation Compose представљена је од стране Google-а 2021. године као алтернатива Fragment-базираној навигацији за Jetpack Compose. Главна предност — потпуна компатибилност са Compose парадигмом: composable() ради у истом животном циклусу као и остале Compose компоненте, без потребе за FragmentManager-ом или трансакцијама. Ово елиминише класу грешака повезаних са неусаглашеношћу животног циклуса Fragment-а и Compose-а.
Сваки composable() прима текстуалну путању (route) и ламбда функцију која добија NavBackStackEntry објекат и враћа Composable UI. Унутар ламбде може се приступити NavController-у кроз poziv navController из опсега, што омогућава организовање прелазака на друге екране. Оваква архитектура чини навигацију експлицитном и предвидивом.
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home"
) {
composable("home") {
HomeScreen(
onNavigateToProfile = {
navController.navigate("profile")
}
)
}
composable("profile") {
ProfileScreen(
onBack = { navController.popBackStack() }
)
}
}
}
Сваки позив composable() ствара у унутрашњем графу NavHost-а чвор са јединственим идентификатором путање. Када NavController изврши navigate(), библиотека упоређује захтевану путању са свим регистрованим composable чворовима и проналази одговарајући. Након поклапања, ствара се NavBackStackEntry који се поставља на стек навигације и покреће се композиција UI-ја.
Унутрашња имплементација composable() користи механизам лење иницијализације: композиција екрана се дешава тек у тренутку првог преласка на ову путању. То значи да екрани на које корисник никада није прешао не заузимају меморију и не извршавају никакав код. Овакав приступ значајно побољшава перформансе апликација са великим бројем екрана.
Параметар key у composable() омогућава управљање поновним стварањем екрана. Подразумевано, composable се не ствара поново при поновном преласку на исту путању — NavHost користи постојећи унос на стеку. Међутим, ако се key проследи и промени, NavHost ствара нову инстанцу composable функције. Ово је корисно за екране са динамичким подацима где је потребно присилно ажурирање стања при поновном отварању.
val NavGraphBuilder.Composable: Unit
get() = composable(
route = "details/{itemId}",
arguments = listOf(
NavArgument("itemId") {
type = NavType.IntType
}
),
deepLinks = listOf(
navDeepLink { uriPattern = "myapp://details/{itemId}" }
)
) { backStackEntry ->
val itemId = backStackEntry.arguments?.getInt("itemId") ?: 0
DetailsScreen(itemId = itemId)
}
composable() подржава флексибилан систем аргумената кроз параметар arguments. Сваки аргумент се описује NavArgument објектом који дефинише тип, подразумевану вредност и обавезност. Аргументи се прослеђују у путањи као path параметри (кроз витичасте заграде) или query параметри (кроз упитник).
Path параметри се наводе директно у шаблону путање: "profile/{userId}". При преласку на путању "profile/42", NavHost аутоматски издваја вредност 42 и чини је доступном кроз backStackEntry.arguments. Query параметри се додају након упитника: "search?query={text}" и такође се аутоматски парсирају од стране библиотеке.
При издвајању аргумената важно је проверити обавезност параметра кроз NavType.isNullableAllowed и обезбедити подразумеване вредности кроз NavArgument defaultValue. Ако обавезни параметар недостаје, Navigation Compose генерише IllegalArgumentException изузетак, што спречава неприметне грешке са нетачним путањама.
| Тип аргумента | NavType | Пример у путањи |
|---|---|---|
| Int | NavType.IntType | "item/{id}" |
| String | NavType.StringType | "user/{name}" |
| Boolean | NavType.BoolType | "filter?enabled={value}" |
| Float | NavType.FloatType | "map/{lat}/{lon}" |
| Long | NavType.LongType | "article/{timestamp}" |
За прослеђивање сложених објеката препоручује се коришћење NavType.ParcelableType или NavType.SerializableType. Ипак, Google саветује да се минимизира величина прослеђених података — боље је проследити идентификатор и учитати објекат по идентификатору унутар екрана. Ово спречава проблеме са великим серијализованим подацима и поједностављује обраду промена конфигурације.
data class Profile(val id: Int, val name: String) : Parcelable
// Крећи се са минималним подацима
navController.navigate("profile/42")
// Преузми аргументе на екрану
composable(
route = "profile/{userId}",
arguments = listOf(
NavArgument("userId") { type = NavType.IntType }
)
) { backStackEntry ->
val userId = backStackEntry.arguments?.getInt("userId") ?: 0
ProfileDetailScreen(userId = userId)
}
У стварним апликацијама често је потребно организовати угнежђене графове навигације — на пример, одвојени стек екрана унутар картице BottomNavigation. composable() подржава угнежђеност кроз механизам угнежђених NavHost-ова: унутар composable екрана може се декларисати сопствени NavHost са независним стеком путања.
Сваки угнежђени NavHost има сопствени NavController и back stack. То значи да навигација унутар картице не утиче на навигацију у другим картицама — корисник може слободно да се пребацује између картица без губљења историје прелазака унутар сваке од њих. Оваква архитектура се назива Scoped Navigation и препоручује је Google за апликације са сложеном вишеслојном навигацијом.
При имплементацији угнежђене навигације важно је правилно управљати стањем NavController-а: сваки угнежђени NavHost треба да чува свој rememberNavController унутар опсега composable функције. Према подацима Android Developer Summit 2024, више од 40% апликација на Jetpack Compose-у са три и више картица користи архитектуру угнежђених NavHost-ова за изолацију навигације између модула.
// Главни NavHost са картицама
composable("tabs") {
MainTabsScreen { tab ->
when (tab) {
Tab.Home -> HomeNavGraph()
Tab.Search -> SearchNavGraph()
}
}
}
// Угнежђени граф у Home картици
@Composable
fun HomeNavGraph() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home_feed"
) {
composable("home_feed") { FeedScreen() }
composable("home_detail/{postId}") { PostDetailScreen() }
}
}
Пре Jetpack Compose-а стандардни начин навигације у Android-у користио је Intent и FragmentManager. Intent — системска порука која покреће нову Activity, што подразумева поновно стварање целог View стабла. За разлику од овога, composable() ради унутар једне Activity и једноставно замењује део Compose стабла, што је значајно брже и ефикасније по меморији.
Главне разлике између composable() и Intent-базиране навигације:
| Карактеристика | composable() | Intent / Fragment |
|---|---|---|
| Архитектура | Single Activity, Compose стабло | Multi Activity, Fragment стекови |
| Пренос података | path/query параметри, заједнички ViewModel | Intent extras, Bundle, SharedPreferences |
| Дубоки линкови | Уграђена подршка navDeepLink | intent-filter у манифесту |
| Back stack | Аутоматско управљање popBackStack | FragmentManager.popBackStack() |
| Време пребацивања | 5–15 ms (унутар процеса) | 50–200 ms (са поновним стварањем) |
Прелазак са Intent на composable() — није само промена API-ја, већ промена архитектонске парадигме. Уместо експлицитног навођења која Activity треба да се отвори, програмер декларативно описује све могуће путање на једном месту, што побољшава читљивост кода и поједностављује тестирање навигације. Према подацима Google I/O 2024, Jetpack Compose са Navigation Compose-ом смањује количину кода за навигацију за 40–60% у поређењу са FragmentManager-ом.
Једна од најчешћих грешака — поновно стварање NavController-а при рекомпозицији. Ако се NavController креира кроз rememberNavController() на нивоу родитељског composable-а који се може поново створити при промени стања, навигација се ломи — историја прелазака се губи. Правилно решење је подизање NavController-а на ниво стабилног composable-а, на пример, на ниво Activity или коренског composable-а апликације.
Други чест проблем — бесконачна рекомпозиција при навигацији. Ово се дешава када се poziv navController.navigate() налази директно у телу composable функције. Пошто навигација мења стање NavHost-а, ово покреће рекомпозицију која поново позива navigate(), стварајући циклус. Сви позиви навигације морају бити умотани у ламбда-руковаоце (onClick, onButtonPressed), а не извршавани у композицији.
Трећа грешка — неправилно управљање back stack-ом при коришћењу BottomNavigation. Једноставна навигација кроз navigate() при сваком пребацивању картице додаје нови унос у стек, уместо да се враћа постојећем. За BottomNavigation треба користити navController.navigate() са restoreState = true и launchSingleTop = true, што обезбеђује правилно враћање стања при пребацивању картица.
fun NavController.navigateToTab(route: String) {
navigate(route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
Често постављана питања
composable() — није анотација, већ функција-проширење NavHost-а која повезује путању са UI-јем. Обична @Composable функција само описује распоред, док composable() региструје тај распоред у навигационом графу са наведеном путањом, чинећи га доступним за навигацију кроз NavController.
Препоручује се прослеђивање само идентификатора (ID) кроз path параметар, а сам објекат учитавати на екрану по ID-у кроз репозиторијум или ViewModel. Ако објекат ипак треба проследити, користите NavType.ParcelableType, али избегавајте прослеђивање објеката већих од 1 KB — то може довести до TransactionTooLargeException.
Ротација екрана изазива промену конфигурације која подразумевано поново ствара Activity. Да бисте сачували стање composable екрана, користите rememberSaveable за једноставне податке или ViewModel са опсегом датог екрана. Navigation Compose враћа back stack након поновног стварања, али се стање унутар composable() функција ресетује без rememberSaveable.
Не, composable() — је функција-проширење NavGraphBuilder-а која је доступна само унутар NavHost блока. За једноставну замену дела UI-ја без навигације користите условно приказивање (when, if) или AnimatedContent. composable() је намењен управо за рутирање са подршком за back stack и дубоке линкове.
Користите SavedStateHandle унутар ViewModel-а: при првом преласку handle.get("initialized") ће вратити null, при повратку — сачувану вредност. Алтернативно, анализирајте тренутну позицију у back stack-у кроз navController.previousBackStackEntry — ако је null, то је први екран у стеку навигације.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође