Adnotarea @Composable — elementul fundamental al Jetpack Compose care transformă o funcție obișnuită Kotlin într-un bloc constructiv declarativ al interfeței de utilizator. Fără această adnotare nu se poate crea niciun ecran în dezvoltarea modernă Android. Potrivit Google Android Developers, 2026, peste 80% din proiectele noi pe Kotlin folosesc Compose pentru construirea UI, iar @Composable este cea mai des utilizată adnotare din ecosistem.
Principalele
@Composable — este o adnotare a limbajului Kotlin care marchează o funcție ca fiind destinată descrierii interfeței de utilizator în framework-ul Jetpack Compose. Când compilatorul Kotlin întâlnește această adnotare, generează cod suplimentar care permite funcției să lucreze în contextul compoziției — sistemul de gestionare a arborelui UI.
Adnotarea @Composable a fost introdusă de Google în 2021 odată cu prima versiune stabilă Jetpack Compose 1.0. Înainte de apariția sa, dezvoltarea interfețelor pentru Android se făcea exclusiv prin marcare XML și sistemul View. @Composable a schimbat radical abordarea: în loc să descrie UI într-un fișier de marcare separat, programatorul scrie interfața direct în Kotlin.
Principala diferență între @Composable și funcțiile obișnuite Kotlin — capacitatea de a citi și reacționa la modificările stării. Când o variabilă citită de o funcție Composable își schimbă valoarea, sistemul programează automat repornirea (recompunerea) acelei funcții. Acest lucru ®l scapă pe programator de actualizarea manuală a UI prin findViewById și setText.
Mecanica internă @Composable se bazează pe conceptul de slot — o zonă specială de memorie alocată fiecărei funcții în cadrul compoziției. În acest slot sunt stocate valorile transmise funcției, precum și informațiile de serviciu necesare pentru comparare la apelurile ulterioare.
Pentru a declara o funcție Composable, este suficient să adăugați adnotarea @Composable înaintea cuvântului cheie fun. Funcția trebuie să se afle într-un pachet care importă adnotarea din androidx.compose.runtime. Numele funcției se recomandă a fi scris cu majusculă — aceasta este o convenție general acceptată în comunitatea Compose, care diferențiază vizual componentele UI de funcțiile obișnuite.
import androidx.compose.runtime.Composable
@Composable
fun Greeting(name: String) {
var count by remember { mutableStateOf(0) }
Column {
Text("Salut, $name!")
Button(onClick = { count++ }) {
Text("Apăsat de $count ori")
}
}
}
Parametrii funcției Composable pot fi orice — tipuri primitive, șiruri, lambde și chiar alte funcții Composable transmise prin Slot API. Se recomandă să faceți parametrii imutabili (val) pentru a evita efectele secundare la recompunere. Toate datele mutabile trebuie gestionate prin mecanismele de stare ale Compose.
Funcțiile Composable nu pot returna valori arbitrare ca funcțiile obișnuite — singura lor sarcină este să construiască sau să actualizeze un fragment din arborele UI. Există totuși modele speciale precum State Hoisting, în care funcția Compose primește starea și callback-urile prin parametri, rămânând pură și reutilizabilă.
Sistemul Compose impune câteva restricții stricte privind modul în care funcțiile Composable trebuie să arate și să se comporte. Prima regulă: o funcție Composable poate apela doar alte funcții Composable sau funcții obișnuite fără efecte secundare. Aceasta asigură predictibilitatea compoziției și funcționarea corectă a optimizărilor Compose.
A doua regulă se referă la ordinea de executare. Compose are dreptul să apeleze funcțiile Composable în orice ordine, deci codul din corpul unei astfel de funcții nu trebuie să se bazeze pe succesiunea de apelare a funcțiilor vecine. Fiecare funcție Composable trebuie să fie autosuficientă la nivelul poziției sale în arborele UI.
A treia regulă — interzicerea efectelor secundare în interiorul corpului funcției Composable. Operații precum scrierea în baza de date, trimiterea de cereri de rețea sau modificarea variabilelor externe trebuie efectuate doar în interiorul efectelor speciale LaunchedEffect, DisposableEffect sau SideEffect. Încălcarea acestei reguli duce la un comportament imprevizibil la recompuneri.
A patra regulă: funcțiile Composable trebuie să fie idempotente. Reapelarea cu aceleași argumente trebuie să producă același UI. Această cerință este necesară pentru funcționarea corectă a optimizării de skipping, în care Compose omite redesenarea funcțiilor ale căror date de intrare nu s-au schimbat.
// Corect: funcție Composable pură fără efecte secundare
@Composable
fun UserCard(user: User, onClick: () -> Unit) {
Card(modifier = Modifier.clickable { onClick() }) {
Text(text = user.name)
}
}
// Greșit: efect secundar în interiorul corpului
@Composable
fun WrongCard(userId: String) {
// val result = viewModel.loadUser(userId) // NEADMIS
Text("Se încarcă...")
}
Să analizăm un exemplu practic de creare a unui ecran de profil folosind adnotarea @Composable. Aici demonstrăm combinarea mai multor funcții Composable, lucrul cu starea și modificatorii — elemente cheie ale oricărui layout Compose.
@Composable
fun ProfileScreen(userId: String) {
var isFollowed by remember { mutableStateOf(false) }
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
ProfileHeader(userId = userId)
Spacer(modifier = Modifier.height(16.dp))
StatsRow(posts = 42, followers = 1280)
Spacer(modifier = Modifier.height(24.dp))
FollowButton(
isFollowed = isFollowed,
onToggle = { isFollowed = !isFollowed }
)
}
}
@Composable
fun ProfileHeader(userId: String) {
Row(verticalAlignment = Alignment.CenterVertically) {
AsyncImage(model = "https://example.com/avatars/$userId",
contentDescription = "User avatar")
Spacer(modifier = Modifier.width(12.dp))
Text(text = "Utilizator #$userId", style = MaterialTheme.typography.headlineMedium)
}
}
@Composable
fun StatsRow(posts: Int, followers: Int) {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
StatItem("Posts", posts)
StatItem("Followers", followers)
}
}
@Composable
fun StatItem(label: String, value: Int) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "$value", style = MaterialTheme.typography.headlineSmall)
Text(text = label, style = MaterialTheme.typography.bodySmall)
}
}
În exemplu, fiecare funcție Composable este responsabilă pentru partea sa de ecran: ProfileScreen gestionează starea generală și compoziția funcțiilor copil, ProfileHeader afișează avatarul și numele, iar StatsRow — blocul cu statistici. Această abordare urmează principiul responsabilității unice și simplifică reutilizarea componentelor.
În Jetpack Compose există trei tipuri principale de funcții Composable. Primul tip — containere (Row, Column, Box, LazyColumn) care determină amplasarea elementelor copil. Al doilea — elemente de afișare (Text, Image, Icon, Button) care renderizează componente UI specifice. Al treilea — funcții Composable personalizate care combină componentele încorporate în blocuri reutilizabile.
Containerele se deosebesc de elementele obișnuite prin faptul că acceptă o lambda content — ultimul parametru de tip @Composable () -> Unit. Tocmai acest mecanism permite construirea de arbori UI imbricați: fiecare container generează o compoziție copil cu propriul context și zonă de memorie.
Funcțiile Composable personalizate se împart în două subtipuri: inteligente (smart) și simple (dumb). Funcțiile inteligente gestionează starea și logica — conțin apeluri către remember, LaunchedEffect și alte API-uri Compose. Funcțiile simple primesc toate datele prin parametri și doar le afișează. Împărțirea în componente inteligente și simple îmbunătățește testabilitatea și reutilizarea codului.
| Tip | Exemplu | Destinație |
|---|---|---|
| Container | Column, Row, Box | Gestionarea amplasării elementelor copil |
| Element | Text, Image, Button | Afișarea conținutului și procesarea intrărilor |
| Personalizat | ProfileCard, UserList | Combinație de componente standard |
Principalul avantaj al adnotării @Composable — posibilitatea de a crea componente UI reutilizabile fără moștenire și ierarhii complexe de clase. Spre deosebire de sistemul View, unde fiecare element personalizat necesita crearea unei clase Java cu constructori, o componentă Composable este doar o funcție Kotlin cu parametri.
Pentru a asigura reutilizarea, se aplică modelul Slot API, în care funcția Composable primește lambde content pentru diferite zone ale layout-ului său. De exemplu, componenta Card poate primi content separat pentru antet, corp și partea inferioară, ceea ce o face universală pentru orice ecrane ale aplicației.
Modificatorii (Modifier) joacă un rol cheie în reutilizare: ei permit ajustarea spațierilor, dimensiunilor, click-urilor și animațiilor fără a modifica componenta în sine. Se recomandă să transmiteți întotdeauna Modifier ca parametru al funcției Composable cu valoare implicită: Modifier = Modifier — aceasta este o practică standard adoptată în bibliotecile oficiale Google.
@Composable
fun SectionCard(
modifier: Modifier = Modifier,
title: String,
content: @Composable () -> Unit
) {
Card(modifier = modifier) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = title, style = MaterialTheme.typography.titleMedium)
Spacer(modifier = Modifier.height(8.dp))
content()
}
}
}
Datorită Slot API, componenta SectionCard poate fi utilizată pe diferite ecrane cu conținut diferit — formulare, liste, blocuri text. Combinarea modificatorilor și Slot API face componentele Compose maxim de flexibile fără a pierde siguranța tipurilor pe care o asigură Kotlin.
Întrebări frecvente
O funcție @Composable se execută în contextul compoziției și poate citi starea, repornind automat la modificarea acesteia. Funcțiile obișnuite Kotlin nu au acces la mecanismele de urmărire a stării și nu participă la construirea arborelui UI.
Nu, funcțiile Composable pot fi apelate doar din alte funcții Composable, deoarece este necesar un context special de compoziție. Pentru integrarea codului Compose cu Kotlin obișnuit se utilizează metoda setContent { } în Activity sau ComposeView în sistemul View.
Este o convenție de denumire adoptată în comunitatea Compose. Majuscula diferențiază vizual componentele UI de funcțiile obișnuite, urmând regulile de denumire a claselor. Nu este o cerință a compilatorului, ci o practică recomandată în documentația Google.
Nu există limită de număr. În practică, un ecran mare poate conține 50–100 de funcții Composable, inclusiv componente încorporate (Text, Button) și personalizate. Compose optimizează arborele de funcții și execută doar pe cele ale căror date de intrare s-au schimbat.
De obicei funcțiile Composable returnează Unit, deoarece sarcina lor este să construiască UI. Există totuși funcții specializate precum remember și derivedStateOf care sunt marcate cu @Composable și returnează valori. Aceasta este o excepție, nu o regulă.
Concluzii
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.
Citiți și