@Composable: co to jest, adnotacja Compose i zakres zastosowania

Autor: IT Sectr Opublikowano: 2026-06-27 Czas czytania: 8 min

Adnotacja @Composable — fundamentalny element Jetpack Compose, który zamienia zwykłą funkcję Kotlin w deklaratywny blok konstrukcyjny interfejsu użytkownika. Bez tej adnotacji nie można utworzyć żadnego ekranu we współczesnym programowaniu na Androida. Według Google Android Developers, 2026, ponad 80% nowych projektów w Kotlin używa Compose do budowy UI, a @Composable jest najczęściej używaną adnotacją w ekosystemie.

Najważniejsze

  • @Composable — adnotacja Kotlin, która pozwala funkcji opisywać UI deklaratywnie
  • Funkcje Composable mogą wywoływać tylko inne funkcje Composable, przestrzegając kontekstu kompozycji
  • Ponowne uruchamianie funkcji Composable następuje przy zmianie parametrów wejściowych lub stanu
  • Kolejność wywołania funkcji Composable nie jest gwarantowana — Compose optymalizuje przebudowę UI
  • Nazewnictwo funkcji Composable podlega zasadzie PascalCase, jak wszystkie komponenty w Compose

Co to jest @Composable w Jetpack Compose

@Composable — to adnotacja języka Kotlin, która oznacza funkcję jako przeznaczoną do opisywania interfejsu użytkownika w frameworku Jetpack Compose. Gdy kompilator Kotlin napotka tę adnotację, generuje dodatkowy kod umożliwiający funkcji działanie w kontekście kompozycji — systemu zarządzania drzewem UI.

Adnotacja @Composable została wprowadzona przez Google w 2021 roku wraz z pierwszą stabilną wersją Jetpack Compose 1.0. Przed jej pojawieniem się tworzenie interfejsów na Androida odbywało się wyłącznie poprzez znaczniki XML i system View. @Composable radykalnie zmieniła podejście: zamiast opisywać UI w osobnym pliku znaczników, programista pisze interfejs bezpośrednio w Kotlin.

Główna różnica między @Composable a zwykłymi funkcjami Kotlin — zdolność do odczytywania i reagowania na zmiany stanu. Gdy zmienna, którą odczytuje funkcja Composable, zmienia wartość, system automatycznie planuje ponowne uruchomienie (rekompozycję) tej funkcji. To uwalnia programistę od ręcznej aktualizacji UI przez findViewById i setText.

Wewnętrzna mechanika @Composable opiera się na koncepcji slotu — specjalnego obszaru pamięci przydzielanego każdej funkcji w ramach kompozycji. W tym slocie przechowywane są wartości przekazane do funkcji oraz informacje pomocnicze niezbędne do porównania przy kolejnych wywołaniach.

Jak zadeklarować funkcję Composable

Aby zadeklarować funkcję Composable, wystarczy dodać adnotację @Composable przed słowem kluczowym fun. Funkcja musi znajdować się w pakiecie importującym adnotację z androidx.compose.runtime. Nazwę funkcji zaleca się pisać wielką literą — to powszechnie przyjęta konwencja w społeczności Compose, która wizualnie odróżnia komponenty UI od zwykłych funkcji.

kotlin
import androidx.compose.runtime.Composable

@Composable
fun Greeting(name: String) {
    var count by remember { mutableStateOf(0) }
    Column {
        Text("Witaj, $name!")
        Button(onClick = { count++ }) {
            Text("Kliknięto $count razy")
        }
    }
}

Parametry funkcji Composable mogą być dowolne — typy prymitywne, ciągi znaków, lambdy, a nawet inne funkcje Composable przekazywane przez Slot API. Zaleca się czynić parametry niemutowalnymi (val), aby uniknąć efektów ubocznych podczas rekompozycji. Wszystkie zmienne dane powinny być zarządzane przez mechanizmy stanu Compose.

Funkcje Composable nie mogą zwracać dowolnych wartości jak zwykłe funkcje — ich jedynym zadaniem jest zbudowanie lub zaktualizowanie fragmentu drzewa UI. Istnieją jednak specjalne wzorce, takie jak State Hoisting, w którym funkcja Composable przyjmuje stan i callbacki przez parametry, pozostając czystą i wielokrotnego użytku.

Zasady funkcji Composable w Kotlin

System Compose nakłada kilka ścisłych ograniczeń na to, jak powinny wyglądać i działać funkcje Composable. Pierwsza zasada: funkcja Composable może wywoływać tylko inne funkcje Composable lub zwykłe funkcje niemające efektów ubocznych. Zapewnia to przewidywalność kompozycji i prawidłowe działanie optymalizacji Compose.

Druga zasada dotyczy kolejności wykonywania. Compose ma prawo wywoływać funkcje Composable w dowolnej kolejności, dlatego kod w treści takiej funkcji nie powinien polegać na sekwencji wywołań sąsiednich funkcji. Każda funkcja Composable powinna być samowystarczalna na poziomie swojej pozycji w drzewie UI.

Trzecia zasada — zakaz efektów ubocznych wewnątrz samej treści funkcji Composable. Operacje takie jak zapis do bazy danych, wysyłanie zapytań sieciowych czy zmiana zmiennych zewnętrznych muszą być wykonywane tylko wewnątrz specjalnych efektów LaunchedEffect, DisposableEffect lub SideEffect. Naruszenie tej zasady prowadzi do nieprzewidywalnego zachowania podczas rekompozycji.

Czwarta zasada: funkcje Composable powinny być idempotentne. Ponowne wywołanie z tymi samymi argumentami powinno dawać ten sam UI. Jest to wymaganie niezbędne do poprawnego działania optymalizacji skippingu, przy której Compose pomija przerysowywanie funkcji, których dane wejściowe się nie zmieniły.

kotlin
// Poprawnie: czysta funkcja Composable bez efektów ubocznych
@Composable
fun UserCard(user: User, onClick: () -> Unit) {
    Card(modifier = Modifier.clickable { onClick() }) {
        Text(text = user.name)
    }
}

// Źle: efekt uboczny w ciele funkcji
@Composable
fun WrongCard(userId: String) {
    // val result = viewModel.loadUser(userId)  // NIEDOZWOLONE
    Text("Ładowanie...")
}

Przykłady użycia @Composable

Rozważmy praktyczny przykład tworzenia ekranu profilu z użyciem adnotacji @Composable. Demonstrujemy tutaj łączenie kilku funkcji Composable, pracę ze stanem i modyfikatorami — kluczowymi elementami każdego układu Compose.

kotlin
@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 = "Użytkownik #$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)
    }
}

W przykładzie każda funkcja Composable odpowiada za swoją część ekranu: ProfileScreen zarządza ogólnym stanem i kompozycją funkcji potomnych, ProfileHeader wyświetla awatar i nazwę, a StatsRow — blok ze statystykami. Takie podejście stosuje zasadę pojedyńczej odpowiedzialności i ułatwia ponowne użycie komponentów.

Rodzaje funkcji Composable i ich przeznaczenie

W Jetpack Compose istnieją trzy główne typy funkcji Composable. Pierwszy typ — kontenery (Row, Column, Box, LazyColumn), które określają rozmieszczenie elementów potomnych. Drugi — elementy wyświetlania (Text, Image, Icon, Button), które renderują konkretne komponenty UI. Trzeci — niestandardowe funkcje Composable, które łączą wbudowane komponenty w wielokrotnego użytku bloki.

Kontenery różnią się od zwykłych elementów tym, że przyjmują lambdę content — ostatni parametr typu @Composable () -> Unit. To właśnie ten mechanizm umożliwia budowanie zagnieżdżonych drzew UI: każdy kontener tworzy kompozycję potomną z własnym kontekstem i obszarem pamięci.

Niestandardowe funkcje Composable dzielą się na dwa podtypy: inteligentne (smart) i głupie (dumb). Inteligentne funkcje zarządzają stanem i logiką — zawierają wywołania remember, LaunchedEffect i innych API Compose. Głupie funkcje przyjmują wszystkie dane przez parametry i tylko je wyświetlają. Podział na inteligentne i głupie komponenty poprawia testowalność i możliwość ponownego użycia kodu.

TypPrzykładPrzeznaczenie
KontenerColumn, Row, BoxZarządzanie rozmieszczeniem elementów potomnych
ElementText, Image, ButtonWyświetlanie treści i obsługa wprowadzania danych
NiestandardowyProfileCard, UserListPołączenie standardowych komponentów

@Composable i ponowne użycie komponentów

Główną zaletą adnotacji @Composable jest możliwość tworzenia wielokrotnego użytku komponentów UI bez dziedziczenia i złożonych hierarchii klas. W przeciwieństwie do systemu View, gdzie każdy niestandardowy element wymagał utworzenia klasy Java z konstruktorami, komponent Composable to po prostu funkcja Kotlin z parametrami.

Aby zapewnić możliwość ponownego użycia, stosuje się wzorzec Slot API, w którym funkcja Composable przyjmuje lambdy content dla różnych obszarów swojego układu. Na przykład komponent Card może przyjmować osobne content dla nagłówka, treści i dolnej części, co czyni go uniwersalnym dla dowolnych ekranów aplikacji.

Modyfikatory (Modifier) odgrywają kluczową rolę w ponownym użyciu: umożliwiają dostosowywanie odstępów, rozmiarów, kliknięć i animacji bez zmiany samego komponentu. Zaleca się zawsze przekazywać Modifier jako parametr funkcji Composable z wartością domyślną: Modifier = Modifier — to standardowa praktyka przyjęta w oficjalnych bibliotekach Google.

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

Dzięki Slot API komponent SectionCard można używać na różnych ekranach z różną zawartością — formularzami, listami, blokami tekstowymi. Połączenie modyfikatorów i Slot API czyni komponenty Compose maksymalnie elastycznymi bez utraty bezpieczeństwa typów, które zapewnia Kotlin.

Często zadawane pytania

Czym @Composable różni się od zwykłej funkcji Kotlin?

@Composable funkcja jest wykonywana w kontekście kompozycji i może odczytywać stan, automatycznie uruchamiając się ponownie przy jego zmianie. Zwykłe funkcje Kotlin nie mają dostępu do mechanizmów śledzenia stanu i nie uczestniczą w budowaniu drzewa UI.

Czy można wywołać funkcję Composable ze zwykłej funkcji?

Nie, funkcji Composable można wywoływać tylko z innych funkcji Composable, ponieważ wymagany jest specjalny kontekst kompozycji. Do integracji kodu Compose ze zwykłym Kotlin używa się metody setContent { } w Activity lub ComposeView w systemie View.

Dlaczego funkcje Composable pisze się wielką literą?

To konwencja nazewnictwa przyjęta w społeczności Compose. Wielka litera wizualnie odróżnia komponenty UI od zwykłych funkcji, zgodnie z zasadami nazewnictwa klas. Nie jest to wymóg kompilatora, ale zalecana praktyka w dokumentacji Google.

Ile funkcji Composable może być na jednym ekranie?

Nie ma ograniczeń co do liczby. W praktyce duży ekran może zawierać 50–100 funkcji Composable, w tym wbudowane komponenty (Text, Button) i niestandardowe. Compose optymalizuje drzewo funkcji i wykonuje tylko te, których dane wejściowe się zmieniły.

Czy funkcja Composable może zwracać wartość?

Zazwyczaj funkcje Composable zwracają Unit, ponieważ ich zadaniem jest budowanie UI. Istnieją jednak wyspecjalizowane funkcje, takie jak remember i derivedStateOf, które są oznaczone @Composable i zwracają wartości. To wyjątek, a nie reguła.

Podsumowanie

  • @Composable — adnotacja do deklaratywnego opisu UI w Jetpack Compose
  • Funkcje Composable są wywoływane tylko wewnątrz innych funkcji Composable w specjalnym kontekście
  • Idempotentność — każde ponowne wykonanie z tymi samymi argumentami daje identyczny UI
  • Efekty uboczne są zabronione w treści funkcji — tylko przez LaunchedEffect i SideEffect
  • Slot API i Modifier zapewniają ponowne użycie komponentów bez dziedziczenia
  • Funkcje kontenerowe (Row, Column, LazyColumn) przyjmują lambdy content dla elementów zagnieżdżonych
  • Zalecenie: przekazuj Modifier jako parametr każdej niestandardowej funkcji Composable z wartością domyślną

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również