Die @Composable-Annotation ist ein grundlegendes Element von Jetpack Compose, das eine gewöhnliche Kotlin-Funktion in einen deklarativen Baustein der Benutzeroberfläche verwandelt. Ohne diese Annotation ist es unmöglich, einen Bildschirm in der modernen Android-Entwicklung zu erstellen. Laut Google Android Developers, 2026 verwenden mehr als 80% der neuen Kotlin-Projekte Compose für die UI-Erstellung, und @Composable ist die am häufigsten verwendete Annotation im Ökosystem.
Wichtige Punkte
@Composable ist eine Annotation der Sprache Kotlin, die eine Funktion als für die Beschreibung der Benutzeroberfläche im Jetpack Compose-Framework markiert. Wenn der Kotlin-Compiler diese Annotation findet, generiert er zusätzlichen Code, der es der Funktion ermöglicht, im Kompositionskontext — dem UI-Baum-Verwaltungssystem — zu arbeiten.
Die @Composable-Annotation wurde von Google im Jahr 2021 zusammen mit der ersten stabilen Version von Jetpack Compose 1.0 eingeführt. Vor ihrem Erscheinen erfolgte die Android-Schnittstellenentwicklung ausschließlich über XML-Markup und das View-System. @Composable hat den Ansatz grundlegend verändert: Anstatt die UI in einer separaten Markup-Datei zu beschreiben, schreibt der Entwickler die Oberfläche direkt in Kotlin.
Der Hauptunterschied zwischen @Composable und gewöhnlichen Kotlin-Funktionen ist die Fähigkeit, Zustand zu lesen und auf Änderungen zu reagieren. Wenn eine Variable, die eine Composable-Funktion liest, ihren Wert ändert, plant das System automatisch einen Neustart (Neuzusammensetzung) dieser Funktion. Dies befreit den Entwickler vom manuellen Aktualisieren der UI über findViewById und setText.
Die interne Mechanik von @Composable basiert auf dem Slot-Konzept — einem speziellen Speicherbereich, der für jede Funktion innerhalb der Komposition reserviert wird. Dieser Slot speichert die an die Funktion übergebenen Werte sowie die Dienstinformationen, die für den Vergleich bei nachfolgenden Aufrufen erforderlich sind.
Um eine Composable-Funktion zu deklarieren, fügt man einfach die @Composable-Annotation vor dem Schlüsselwort fun hinzu. Die Funktion muss sich in einem Paket befinden, das die Annotation aus androidx.compose.runtime importiert. Es wird empfohlen, den Funktionsnamen mit einem Großbuchstaben zu beginnen — dies ist eine weit verbreitete Konvention in der Compose-Community, die UI-Komponenten visuell von gewöhnlichen Funktionen unterscheidet.
import androidx.compose.runtime.Composable
@Composable
fun Greeting(name: String) {
var count by remember { mutableStateOf(0) }
Column {
Text("Hallo, $name!")
Button(onClick = { count++ }) {
Text("$count Mal geklickt")
}
}
}
Die Parameter einer Composable-Funktion können beliebig sein — primitive Typen, Zeichenketten, Lambdas und sogar andere Composable-Funktionen, die über die Slot-API übergeben werden. Empfohlen wird, Parameter unveränderlich (val) zu machen, um Nebeneffekte während der Neuzusammensetzung zu vermeiden. Alle veränderlichen Daten sollten über die Zustandsmechanismen von Compose verwaltet werden.
Composable-Funktionen können keine beliebigen Werte wie reguläre Funktionen zurückgeben — ihre einzige Aufgabe ist es, einen Teil des UI-Baums zu erstellen oder zu aktualisieren. Es gibt jedoch spezielle Muster wie State Hoisting, bei dem eine Compose-Funktion Zustand und Callbacks über Parameter annimmt und dabei rein und wiederverwendbar bleibt.
Das Compose-System legt mehrere strenge Einschränkungen fest, wie Composable-Funktionen aussehen und sich verhalten sollen. Die erste Regel: Eine Composable-Funktion darf nur andere Composable-Funktionen oder reguläre Funktionen ohne Nebeneffekte aufrufen. Dies gewährleistet die Vorhersagbarkeit der Komposition und die korrekte Funktionsweise der Compose-Optimierungen.
Die zweite Regel betrifft die Ausführungsreihenfolge. Compose hat das Recht, Composable-Funktionen in beliebiger Reihenfolge aufzurufen, daher darf der Code im Körper einer solchen Funktion nicht von der Aufrufsequenz benachbarter Funktionen abhängen. Jede Composable-Funktion muss auf der Ebene ihrer Position im UI-Baum autark sein.
Die dritte Regel — Verbot von Nebeneffekten innerhalb des Körpers einer Composable-Funktion. Operationen wie Datenbankzugriffe, Netzwerkanfragen oder das Ändern externer Variablen dürfen nur innerhalb spezieller Effekte ausgeführt werden: LaunchedEffect, DisposableEffect oder SideEffect. Ein Verstoß gegen diese Regel führt zu unvorhersehbarem Verhalten bei Neuzusammensetzungen.
Die vierte Regel: Composable-Funktionen müssen idempotent sein. Ein erneuter Aufruf mit denselben Argumenten sollte dieselbe UI erzeugen. Diese Anforderung ist für die korrekte Funktionsweise der Skipping-Optimierung notwendig, bei der Compose das Neuzeichnen von Funktionen überspringt, deren Eingabedaten sich nicht geändert haben.
// Richtig: reine Composable-Funktion ohne Nebeneffekte
@Composable
fun UserCard(user: User, onClick: () -> Unit) {
Card(modifier = Modifier.clickable { onClick() }) {
Text(text = user.name)
}
}
// Falsch: Nebeneffekt innerhalb des Körpers
@Composable
fun WrongCard(userId: String) {
// val result = viewModel.loadUser(userId) // NICHT ERLAUBT
Text("Lädt...")
}
Betrachten wir ein praktisches Beispiel für die Erstellung eines Profilbildschirms mit der @Composable-Annotation. Hier zeigen wir die Kombination mehrerer Composable-Funktionen, die Arbeit mit Zustand und Modifikatoren — Schlüsselelemente jedes Compose-Layouts.
@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 = "Benutzer #$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)
}
}
Im Beispiel ist jede Composable-Funktion für ihren Teil des Bildschirms verantwortlich: ProfileScreen verwaltet den Gesamtzustand und die Komposition der Kindfunktionen, ProfileHeader zeigt Avatar und Namen an, und StatsRow zeigt einen Statistikblock. Dieser Ansatz folgt dem Prinzip der einzelnen Verantwortung und vereinfacht die Wiederverwendung von Komponenten.
In Jetpack Compose gibt es drei Haupttypen von Composable-Funktionen. Der erste Typ — Container (Row, Column, Box, LazyColumn) — bestimmen die Anordnung der Kindelemente. Der zweite Typ — Anzeigeelemente (Text, Image, Icon, Button) — rendern bestimmte UI-Komponenten. Der dritte Typ — benutzerdefinierte Composable-Funktionen — kombinieren eingebaute Komponenten zu wiederverwendbaren Blöcken.
Container unterscheiden sich von regulären Elementen dadurch, dass sie ein Content-Lambda akzeptieren — den letzten Parameter vom Typ @Composable () -> Unit. Dieser Mechanismus ermöglicht den Aufbau verschachtelter UI-Bäume: Jeder Container erzeugt eine Kindkomposition mit eigenem Kontext und Speicherbereich.
Benutzerdefinierte Composable-Funktionen werden in zwei Untertypen unterteilt: smarte (smart) und stumme (dumb). Smarte Funktionen verwalten Zustand und Logik — sie enthalten Aufrufe von remember, LaunchedEffect und anderen Compose-APIs. Stumme Funktionen erhalten alle Daten über Parameter und zeigen sie nur an. Die Trennung in smarte und stumme Komponenten verbessert die Testbarkeit und die Wiederverwendbarkeit des Codes.
| Typ | Beispiel | Zweck |
|---|---|---|
| Container | Column, Row, Box | Verwaltung der Anordnung von Kindelementen |
| Element | Text, Image, Button | Anzeige von Inhalten und Eingabebehandlung |
| Benutzerdefiniert | ProfileCard, UserList | Kombination von Standardkomponenten |
Der Hauptvorteil der @Composable-Annotation ist die Möglichkeit, wiederverwendbare UI-Komponenten ohne Vererbung und komplexe Klassenhierarchien zu erstellen. Im Gegensatz zum View-System, wo jedes benutzerdefinierte Element die Erstellung einer Java-Klasse mit Konstruktoren erforderte, ist eine Composable-Komponente einfach eine Kotlin-Funktion mit Parametern.
Zur Sicherstellung der Wiederverwendbarkeit wird das Slot-API-Muster verwendet, bei dem eine Composable-Funktion Content-Lambdas für verschiedene Bereiche ihres Layouts akzeptiert. Beispielsweise kann eine Card-Komponente separate Inhalte für Kopfzeile, Hauptteil und Fußzeile akzeptieren, was sie universell für jeden Bildschirm der Anwendung macht.
Modifikatoren (Modifier) spielen eine Schlüsselrolle bei der Wiederverwendbarkeit: Sie ermöglichen die Konfiguration von Abständen, Größen, Klicks und Animationen ohne Änderung der Komponente selbst. Es wird empfohlen, Modifier immer als Parameter einer Composable-Funktion mit einem Standardwert zu übergeben: Modifier = Modifier — dies ist eine Standardpraxis, die in den offiziellen Google-Bibliotheken üblich ist.
@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()
}
}
}
Dank der Slot-API kann die SectionCard-Komponente auf verschiedenen Bildschirmen mit unterschiedlichen Inhalten verwendet werden — Formularen, Listen, Textblöcken. Die Kombination von Modifikatoren und der Slot-API macht Compose-Komponenten äußerst flexibel, ohne die Typsicherheit zu verlieren, die Kotlin bietet.
Häufig gestellte Fragen
Eine @Composable-Funktion wird im Kompositionskontext ausgeführt und kann Zustand lesen, wobei sie bei Änderung automatisch neu startet. Normale Kotlin-Funktionen haben keinen Zugriff auf Zustandsverfolgungsmechanismen und nehmen nicht am Aufbau des UI-Baums teil.
Nein, Composable-Funktionen können nur von anderen Composable-Funktionen aufgerufen werden, da ein spezieller Kompositionskontext erforderlich ist. Zur Integration von Compose-Code mit normalem Kotlin wird die setContent { }-Methode der Activity oder ComposeView im View-System verwendet.
Dies ist eine in der Compose-Community übliche Namenskonvention. Der Großbuchstabe unterscheidet UI-Komponenten visuell von gewöhnlichen Funktionen, indem er den Klassenbenennungsregeln folgt. Es ist keine Compiler-Anforderung, sondern eine empfohlene Praxis in der Google-Dokumentation.
Es gibt keine Begrenzung der Anzahl. In der Praxis kann ein großer Bildschirm 50–100 Composable-Funktionen enthalten, einschließlich eingebauter Komponenten (Text, Button) und benutzerdefinierter. Compose optimiert den Funktionsbaum und führt nur diejenigen aus, deren Eingabedaten sich geändert haben.
Normalerweise geben Composable-Funktionen Unit zurück, da ihre Aufgabe der Aufbau der UI ist. Es gibt jedoch spezialisierte Funktionen wie remember und derivedStateOf, die mit @Composable markiert sind und Werte zurückgeben. Dies ist eine Ausnahme, nicht die Regel.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch