composable(): ce este, NavHost și rutare în Jetpack Compose

Autor: IT Sectr Publicat: 2026-06-30 Timp de citire: 9 min

composable() — este o funcție a bibliotecii Navigation Compose care înregistrează un ecran în NavHost și leagă ruta URL de aspectul Compose. Când navigarea trece pe o rută specificată, Jetpack Compose apelează funcția composable corespunzătoare și o afișează ca ecran curent. Spre deosebire de FragmentManager sau navigarea bazată pe Intent, composable() funcționează la nivelul unei singure Activity și este gestionată complet prin Kotlin DSL. Conform datelor Android Developers (2025), peste 73% din aplicațiile Android moderne construite pe Jetpack Compose folosesc exact Navigation Compose pentru organizarea schimbării ecranelor.

Principalele puncte

  • composable() — funcția de înregistrare a ecranului în NavHost a bibliotecii Navigation Compose.
  • Ruta — fiecare ecran este identificat printr-o rută text transmisă ca prim argument.
  • Parametrii — composable() suportă argumente prin NavArgument, inclusiv obligatorii și opționale.
  • Încuibarea — navigarea încubită este suportată prin NavHost-uri încuibite cu grafuri de rute separate.
  • Performanța — composable() utilizează inițializare lazy: ecranul este creat doar la prima navigare.

Ce este composable() în NavHost

composable() — este o funcție de extensie (extension function) a obiectului NavHost. Kotlin DSL permite apelarea acesteia în interiorul blocului NavHost pentru descrierea declarativă a tuturor ecranelor aplicației. Fiecare apel creează o intrare în graful de navigare, legând o rută text de o funcție composable. Când utilizatorul navighează pe o rută specificată, NavHost afișează composable-ul corespunzător ca ecran curent, ascunzându-l pe cel anterior.

Biblioteca Navigation Compose a fost prezentată de Google în 2021 ca alternativă la navigarea bazată pe Fragment pentru Jetpack Compose. Principalul avantaj — compatibilitatea completă cu paradigma Compose: composable() funcționează în același ciclu de viață ca și celelalte componente Compose, fără necesitatea FragmentManager sau a tranzacțiilor. Aceasta elimină o clasă de erori legate de nepotrivirea ciclurilor de viață dintre Fragment și Compose.

Fiecare composable() primește o rută text (route) și o funcție lambda care primește un obiect NavBackStackEntry și returnează un UI Composable. În interiorul lambda se poate accesa NavController prin apelul navController din scope, ceea ce permite organizarea navigărilor către alte ecrane. Această arhitectură face navigarea explicită și predictibilă.

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

Cum funcționează composable(): chei și parametri

Fiecare apel composable() creează în graful intern al NavHost un nod cu un identificator unic de rută. Când NavController execută navigate(), biblioteca compară ruta solicitată cu toate nodurile composable înregistrate și găsește nodul corespunzător. După potrivire, se creează un NavBackStackEntry care este plasat pe stiva de navigare și se inițiază compoziția UI.

Implementarea internă a composable() utilizează mecanismul de inițializare lazy: compoziția ecranului are loc doar în momentul primei navigări pe această rută. Aceasta înseamnă că ecranele pe care utilizatorul nu a navigat niciodată nu ocupă memorie și nu execută niciun cod. O astfel de abordare îmbunătățește semnificativ performanța aplicațiilor cu un număr mare de ecrane.

Parametrul key din composable() permite gestionarea recreării ecranului. În mod implicit, composable nu este recreat la o navigare repetată pe aceeași rută — NavHost folosește intrarea existentă din back stack. Totuși, dacă key este transmis și se modifică, NavHost creează o nouă instanță a funcției composable. Acest lucru este util pentru ecrane cu date dinamice, unde este necesară forțarea actualizării stării la redeschidere.

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

Transmiterea argumentelor prin composable()

composable() suportă un sistem flexibil de argumente prin parametrul arguments. Fiecare argument este descris de un obiect NavArgument care definește tipul, valoarea implicită și obligativitatea. Argumentele sunt transmise în rută ca parametri de cale (prin acolade) sau parametri de query (prin semnul întrebării).

Parametrii de cale sunt indicați direct în șablonul rutei: "profile/{userId}". La navigarea pe ruta "profile/42", NavHost extrage automat valoarea 42 și o face disponibilă prin backStackEntry.arguments. Parametrii de query sunt adăugați după semnul întrebării: "search?query={text}" și sunt, de asemenea, analizați automat de bibliotecă.

La extragerea argumentelor este important să se verifice obligativitatea parametrului prin NavType.isNullableAllowed și să se furnizeze valori implicite prin NavArgument defaultValue. Dacă un parametru obligatoriu lipsește, Navigation Compose generează o excepție IllegalArgumentException, prevenind bug-urile invizibile cu rute incorecte.

Tipul argumentuluiNavTypeExemplu în rută
IntNavType.IntType"item/{id}"
StringNavType.StringType"user/{name}"
BooleanNavType.BoolType"filter?enabled={value}"
FloatNavType.FloatType"map/{lat}/{lon}"
LongNavType.LongType"article/{timestamp}"

Pentru transmiterea obiectelor complexe, se recomandă utilizarea NavType.ParcelableType sau NavType.SerializableType. Cu toate acestea, Google sfătuiește să se minimizeze dimensiunea datelor transmise — este mai bine să se transmită un identificator și să se încarce obiectul după identificator în interiorul ecranului. Aceasta previne problemele cu date serializate mari și simplifică gestionarea schimbărilor de configurare.

kotlin
data class Profile(val id: Int, val name: String) : Parcelable

            // Navighează cu date minime
navController.navigate("profile/42")

            // Preia argumentele pe ecran
composable(
    route = "profile/{userId}",
    arguments = listOf(
        NavArgument("userId") { type = NavType.IntType }
    )
) { backStackEntry ->
    val userId = backStackEntry.arguments?.getInt("userId") ?: 0
    ProfileDetailScreen(userId = userId)
}

Navigarea încubită cu composable()

În aplicațiile reale este adesea necesară organizarea unor grafuri de navigare încuibite — de exemplu, o stivă separată de ecrane în interiorul unei file BottomNavigation. composable() suportă încuibarea prin mecanismul NavHost-urilor încuibite: în interiorul unui ecran composable se poate declara un NavHost propriu cu o stivă independentă de rute.

Fiecare NavHost încubit are propriul său NavController și back stack. Aceasta înseamnă că navigarea în interiorul unei file nu afectează navigarea în alte file — utilizatorul poate comuta liber între file fără a pierde istoricul navigărilor în fiecare dintre ele. O astfel de arhitectură se numește Scoped Navigation și este recomandată de Google pentru aplicații cu navigare complexă pe mai multe niveluri.

La implementarea navigării încuibite, este important să se gestioneze corect starea NavController: fiecare NavHost încubit trebuie să-și stocheze propriul rememberNavController în scope-ul funcției composable. Conform datelor Android Developer Summit 2024, peste 40% din aplicațiile Jetpack Compose cu trei sau mai multe file utilizează arhitectura NavHost-urilor încuibite pentru izolarea navigării între module.

kotlin
// NavHost principal cu file
composable("tabs") {
    MainTabsScreen { tab ->
        when (tab) {
            Tab.Home -> HomeNavGraph()
            Tab.Search -> SearchNavGraph()
        }
    }
}

// Graf încubit în fila Home
@Composable
fun HomeNavGraph() {
    val navController = rememberNavController()
    NavHost(
        navController = navController,
        startDestination = "home_feed"
    ) {
        composable("home_feed") { FeedScreen() }
        composable("home_detail/{postId}") { PostDetailScreen() }
    }
}

Compararea composable() cu navigarea Intent

Înainte de Jetpack Compose, metoda standard de navigare în Android folosea Intent și FragmentManager. Intent — este un mesaj de sistem care lansează o nouă Activity, ceea ce implică recrearea întregului arbore View. Spre deosebire de aceasta, composable() funcționează în interiorul unei singure Activity și pur și simplu înlocuiește o parte din arborele Compose, ceea ce este semnificativ mai rapid și mai eficient din punct de vedere al memoriei.

Principalele diferențe între composable() și navigarea bazată pe Intent:

  • Viteza — composable() comută ecranele în milisecunde fără recrearea Activity, Intent necesită repornirea Activity.
  • Animațiile — în Navigation Compose, animațiile de tranziție se definesc declarativ prin AnimatedNavHost, fără a fi nevoie de overridePendingTransition.
  • Stare partajată — composable() funcționează într-un scope ViewModel comun, ceea ce simplifică transmiterea datelor între ecrane fără Intent extras.
Caracteristicacomposable()Intent / Fragment
ArhitecturaSingle Activity, arbore ComposeMulti Activity, stive Fragment
Transmiterea datelorparametri cale/query, ViewModel partajatIntent extras, Bundle, SharedPreferences
Linkuri profundeSuport încorporat navDeepLinkintent-filter în manifest
Stiva înapoiGestionare automată popBackStackFragmentManager.popBackStack()
Timpul de comutare5–15 ms (în interiorul procesului)50–200 ms (cu recreere)

Trecerea de la Intent la composable() — nu este doar o schimbare de API, ci o schimbare de paradigmă arhitecturală. În loc să se indice explicit care Activity trebuie deschisă, dezvoltatorul descrie declarativ toate rutele posibile într-un singur loc, ceea ce îmbunătățește lizibilitatea codului și simplifică testarea navigării. Conform datelor Google I/O 2024, Jetpack Compose cu Navigation Compose reduce cantitatea de cod pentru navigare cu 40–60% în comparație cu FragmentManager.

Erori tipice cu composable()

Una dintre cele mai frecvente erori — recrearea NavController la recompoziție. Dacă NavController este creat prin rememberNavController() la nivelul composable-ului părinte care poate fi recreat la schimbarea stării, navigarea se strică — istoricul navigărilor se pierde. Soluția corectă este ridicarea NavController la nivelul unui composable stabil, de exemplu, la nivelul Activity sau al composable-ului rădăcină al aplicației.

A doua problemă frecventă — recompoziția infinită la navigare. Aceasta apare atunci când apelul navController.navigate() este plasat direct în corpul funcției composable. Deoarece navigarea modifică starea NavHost, aceasta declanșează o recompoziție care apelează din nou navigate(), creând un ciclu. Toate apelurile de navigare trebuie să fie înfășurate în lambda-handlere (onClick, onButtonPressed), nu executate în compoziție.

A treia eroare — gestionarea incorectă a back stack-ului la utilizarea BottomNavigation. Navigarea simplă prin navigate() la fiecare comutare a filei adaugă o nouă intrare în stivă, în loc să revină la cea existentă. Pentru BottomNavigation trebuie utilizat navController.navigate() cu restoreState = true și launchSingleTop = true, ceea ce asigură restaurarea corectă a stării la comutarea între file.

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

Întrebări frecvente

Care este diferența dintre composable() și o funcție obișnuită @Composable?

composable() — nu este o adnotare, ci o funcție de extensie a NavHost care leagă o rută de UI. O funcție obișnuită @Composable descrie pur și simplu aspectul, iar composable() înregistrează acest aspect în graful de navigare cu ruta specificată, făcându-l disponibil pentru navigare prin NavController.

Cum se transmite un obiect complex între ecranele composable()?

Se recomandă transmiterea doar a identificatorului (ID) prin parametrul de cale, iar obiectul să fie încărcat pe ecran după ID printr-un repository sau ViewModel. Dacă obiectul trebuie totuși transmis, utilizați NavType.ParcelableType, dar evitați transmiterea obiectelor mai mari de 1 KB — acest lucru poate duce la TransactionTooLargeException.

De ce ecranul composable() se recrează la rotirea ecranului?

Rotirea ecranului provoacă o schimbare de configurare care, în mod implicit, recrează Activity. Pentru a păstra starea ecranelor composable, utilizați rememberSaveable pentru date simple sau ViewModel cu scope-ul ecranului respectiv. Navigation Compose restaurează back stack-ul după recreare, dar starea din interiorul funcțiilor composable() se resetează fără rememberSaveable.

Se poate utiliza composable() fără NavHost?

Nu, composable() — este o funcție de extensie a NavGraphBuilder care este disponibilă doar în interiorul blocului NavHost. Pentru înlocuirea simplă a unei părți de UI fără navigare, utilizați randare condiționată (when, if) sau AnimatedContent. composable() este destinat tocmai pentru rutare cu suport pentru back stack și linkuri profunde.

Cum se diferențiază prima navigare de revenirea înapoi în composable()?

Utilizați SavedStateHandle în interiorul ViewModel: la prima navigare, handle.get("initialized") va returna null, la revenire — valoarea salvată. Alternativ, analizați poziția curentă în back stack prin navController.previousBackStackEntry — dacă este null, înseamnă că este primul ecran în stiva de navigare.

Rezumat

  • composable() — funcția de înregistrare a ecranului în NavHost, principalul mod de organizare a navigării în Jetpack Compose.
  • Rutele — fiecare ecran este identificat printr-un șir de rută cu parametri de cale și query opționali.
  • Argumentele — se transmit prin NavArgument cu suport pentru tipuri primitive, Parcelable și Serializable.
  • Încuibarea — composable() suportă NavHost-uri încuibite pentru organizarea navigării modulare cu stive independente.
  • Performanța — inițializarea lazy a ecranelor economisește memorie, timpul de comutare între ecrane este de 5–15 ms.
  • Erori — principalele probleme: recrearea NavController, recompoziția infinită la apelarea navigate() în corpul composable, funcționarea incorectă a BottomNavigation.
  • Migrarea — trecerea de la FragmentManager la composable() reduce volumul codului de navigare cu 40–60% și elimină o clasă de erori legate de ciclul de viață al Fragment.

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.

Discutați proiectul

Citiți și