composable(): шта је то, NavHost и рутирање у Jetpack Compose-у

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

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() — функција регистрације екрана у NavHost-у библиотеке Navigation Compose.
  • Путања — сваки екран се идентификује текстуалном путањом која се прослеђује као први аргумент.
  • Параметри — composable() подржава аргументе кроз NavArgument, укључујући обавезне и опционе.
  • Угнежђеност — подржана је угнежђена навигација кроз угнежђене NavHost-ове са одвојеним графовима путања.
  • Перформансе — composable() користи лењу иницијализацију: екран се ствара тек при првом преласку.

Шта је composable() у NavHost-у

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 из опсега, што омогућава организовање прелазака на друге екране. Оваква архитектура чини навигацију експлицитном и предвидивом.

kotlin
@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(): кључеви и параметри

Сваки позив composable() ствара у унутрашњем графу NavHost-а чвор са јединственим идентификатором путање. Када NavController изврши navigate(), библиотека упоређује захтевану путању са свим регистрованим composable чворовима и проналази одговарајући. Након поклапања, ствара се NavBackStackEntry који се поставља на стек навигације и покреће се композиција UI-ја.

Унутрашња имплементација composable() користи механизам лење иницијализације: композиција екрана се дешава тек у тренутку првог преласка на ову путању. То значи да екрани на које корисник никада није прешао не заузимају меморију и не извршавају никакав код. Овакав приступ значајно побољшава перформансе апликација са великим бројем екрана.

Параметар key у composable() омогућава управљање поновним стварањем екрана. Подразумевано, composable се не ствара поново при поновном преласку на исту путању — NavHost користи постојећи унос на стеку. Међутим, ако се key проследи и промени, NavHost ствара нову инстанцу composable функције. Ово је корисно за екране са динамичким подацима где је потребно присилно ажурирање стања при поновном отварању.

kotlin
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()

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Пример у путањи
IntNavType.IntType"item/{id}"
StringNavType.StringType"user/{name}"
BooleanNavType.BoolType"filter?enabled={value}"
FloatNavType.FloatType"map/{lat}/{lon}"
LongNavType.LongType"article/{timestamp}"

За прослеђивање сложених објеката препоручује се коришћење NavType.ParcelableType или NavType.SerializableType. Ипак, Google саветује да се минимизира величина прослеђених података — боље је проследити идентификатор и учитати објекат по идентификатору унутар екрана. Ово спречава проблеме са великим серијализованим подацима и поједностављује обраду промена конфигурације.

kotlin
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)
}

Угнежђена навигација са composable()

У стварним апликацијама често је потребно организовати угнежђене графове навигације — на пример, одвојени стек екрана унутар картице BottomNavigation. composable() подржава угнежђеност кроз механизам угнежђених NavHost-ова: унутар composable екрана може се декларисати сопствени NavHost са независним стеком путања.

Сваки угнежђени NavHost има сопствени NavController и back stack. То значи да навигација унутар картице не утиче на навигацију у другим картицама — корисник може слободно да се пребацује између картица без губљења историје прелазака унутар сваке од њих. Оваква архитектура се назива Scoped Navigation и препоручује је Google за апликације са сложеном вишеслојном навигацијом.

При имплементацији угнежђене навигације важно је правилно управљати стањем NavController-а: сваки угнежђени NavHost треба да чува свој rememberNavController унутар опсега composable функције. Према подацима Android Developer Summit 2024, више од 40% апликација на Jetpack Compose-у са три и више картица користи архитектуру угнежђених NavHost-ова за изолацију навигације између модула.

kotlin
// Главни 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() }
    }
}

Поређење composable() са Intent навигацијом

Пре Jetpack Compose-а стандардни начин навигације у Android-у користио је Intent и FragmentManager. Intent — системска порука која покреће нову Activity, што подразумева поновно стварање целог View стабла. За разлику од овога, composable() ради унутар једне Activity и једноставно замењује део Compose стабла, што је значајно брже и ефикасније по меморији.

Главне разлике између composable() и Intent-базиране навигације:

  • Брзина — composable() пребацује екране за милисекунде без поновног стварања Activity, Intent захтева поновно покретање Activity.
  • Анимације — у Navigation Compose-у анимације прелазака се задају декларативно кроз AnimatedNavHost, без потребе за overridePendingTransition.
  • Заједничко стање — composable() ради у заједничком ViewModel опсегу, што поједностављује пренос података између екрана без Intent extras-а.
Карактеристикаcomposable()Intent / Fragment
АрхитектураSingle Activity, Compose стаблоMulti Activity, Fragment стекови
Пренос податакаpath/query параметри, заједнички ViewModelIntent extras, Bundle, SharedPreferences
Дубоки линковиУграђена подршка navDeepLinkintent-filter у манифесту
Back stackАутоматско управљање popBackStackFragmentManager.popBackStack()
Време пребацивања5–15 ms (унутар процеса)50–200 ms (са поновним стварањем)

Прелазак са Intent на composable() — није само промена API-ја, већ промена архитектонске парадигме. Уместо експлицитног навођења која Activity треба да се отвори, програмер декларативно описује све могуће путање на једном месту, што побољшава читљивост кода и поједностављује тестирање навигације. Према подацима Google I/O 2024, Jetpack Compose са Navigation Compose-ом смањује количину кода за навигацију за 40–60% у поређењу са FragmentManager-ом.

Типичне грешке са composable()

Једна од најчешћих грешака — поновно стварање 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, што обезбеђује правилно враћање стања при пребацивању картица.

kotlin
fun NavController.navigateToTab(route: String) {
    navigate(route) {
        popUpTo(navController.graph.findStartDestination().id) {
            saveState = true
        }
        launchSingleTop = true
        restoreState = true
    }
}

Често постављана питања

Која је разлика између composable() и обичне @Composable функције?

composable() — није анотација, већ функција-проширење NavHost-а која повезује путању са UI-јем. Обична @Composable функција само описује распоред, док composable() региструје тај распоред у навигационом графу са наведеном путањом, чинећи га доступним за навигацију кроз NavController.

Како проследити сложени објекат између composable() екрана?

Препоручује се прослеђивање само идентификатора (ID) кроз path параметар, а сам објекат учитавати на екрану по ID-у кроз репозиторијум или ViewModel. Ако објекат ипак треба проследити, користите NavType.ParcelableType, али избегавајте прослеђивање објеката већих од 1 KB — то може довести до TransactionTooLargeException.

Зашто се composable() екран поново ствара при ротацији екрана?

Ротација екрана изазива промену конфигурације која подразумевано поново ствара Activity. Да бисте сачували стање composable екрана, користите rememberSaveable за једноставне податке или ViewModel са опсегом датог екрана. Navigation Compose враћа back stack након поновног стварања, али се стање унутар composable() функција ресетује без rememberSaveable.

Може ли се composable() користити без NavHost-а?

Не, composable() — је функција-проширење NavGraphBuilder-а која је доступна само унутар NavHost блока. За једноставну замену дела UI-ја без навигације користите условно приказивање (when, if) или AnimatedContent. composable() је намењен управо за рутирање са подршком за back stack и дубоке линкове.

Како разликовати први прелазак од повратка назад у composable()-у?

Користите SavedStateHandle унутар ViewModel-а: при првом преласку handle.get("initialized") ће вратити null, при повратку — сачувану вредност. Алтернативно, анализирајте тренутну позицију у back stack-у кроз navController.previousBackStackEntry — ако је null, то је први екран у стеку навигације.

Закључак

  • composable() — функција регистрације екрана у NavHost-у, главни начин организовања навигације у Jetpack Compose-у.
  • Путање — сваки екран се идентификује текстом путање са опционим path и query параметрима.
  • Аргументи — прослеђују се кроз NavArgument са подршком за примитивне, Parcelable и Serializable типове.
  • Угнежђеност — composable() подржава угнежђене NavHost за организовање модуларне навигације са независним стековима.
  • Перформансе — лења иницијализација екрана штеди меморију, време пребацивања између екрана износи 5–15 ms.
  • Грешке — главни проблеми: поновно стварање NavController-а, бесконачна рекомпозиција при позиву navigate() у телу composable-а, неправилан рад BottomNavigation-а.
  • Миграција — прелазак са FragmentManager-а на composable() смањује обим кода навигације за 40–60% и елиминише класу грешака повезаних са животним циклусом Fragment-а.

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

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

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

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