Modifier — to niezmienny obiekt w Jetpack Compose, który określa właściwości komponentu UI: rozmiar, odstępy, tło, obsługę gestów i zachowanie. Modyfikatory łączy się w łańcuch przez sekwencyjne wywoływanie, przy czym kolejność ich zastosowania krytycznie wpływa na wynik. Według Google Android Developers, 2026, prawidłowe użycie Modifier jest podstawą budowania elastycznego i wydajnego interfejsu w deklaratywnym UI.
Najważniejsze
Modifier — to interfejs z pakietu androidx.compose.ui, implementujący wzorzec Composite. Każdy modyfikator to element łańcucha, który opakowuje poprzedni i dodaje swoje zachowanie. Modifier jest niezmienny — wszelkie zmiany tworzą nowy obiekt przez kopiowanie z dodaniem nowego elementu do łańcucha. Pozwala to bezpiecznie współdzielić jeden Modifier między wieloma komponentami.
Podstawowe funkcje-modyfikatory wywołuje się przez obiekt-towarzysz Modifier (np. Modifier.padding(), Modifier.fillMaxWidth()). Każda funkcja zwraca nowy Modifier z dodanym elementem. Jeśli jest kilka modyfikatorów, łączą się w łańcuch: Modifier.padding(16.dp).fillMaxWidth().background(Color.Blue). Kolejność to kierunek od zewnątrz do wewnątrz względem elementu UI.
W przeciwieństwie do tradycyjnych View, gdzie właściwości ustawiało się przez settery (view.setPadding(...), view.setBackground(...)), w Compose Modifier to deklaratywny opis. Komponent nie „stosuje” modyfikatorów podczas działania — LayoutNode na etapie kompozycji przechodzi przez łańcuch Modifier i zbiera z niego listę Modifier.Element, które następnie są przetwarzane na etapie pomiaru i rozmieszczania.
Kolejność modyfikatorów — jeden z najczęstszych błędów w Compose. Każdy modyfikator opakowuje poprzedni, a operacje wykonują się od zewnątrz do wewnątrz. Na przykład padding(16.dp).clickable { }: najpierw dodawany jest odstęp wokół elementu, następnie obszar kliknięcia obejmuje również odstęp. clickable { }.padding(16.dp): najpierw obszar kliknięcia równa się rozmiarowi elementu, potem odstęp jest dodawany wokół — kliknięcie w odstępie nie zadziała.
Zasada zapamiętywania: czytaj łańcuch od lewej do prawej i stosuj od zewnątrz do wewnątrz. Pierwszy modyfikator — najbardziej zewnętrzny, stosowany do obszaru wokół elementu. Ostatni — najbardziej wewnętrzny, stosowany bezpośrednio do treści. Modyfikatory rozmiaru (size, fillMaxWidth) powinny iść po odstępach, jeśli odstęp jest potrzebny od rodzica, lub przed odstępami, jeśli treść ma być najpierw ograniczona, a potem wyśrodkowana.
Przykład: size(100.dp).padding(10.dp) — element o stałym rozmiarze 100dp, następnie padding 10dp na zewnątrz (ostateczny rozmiar 120dp). padding(10.dp).size(100.dp) — padding 10dp zmniejsza dostępną przestrzeń do (rodzic - 20dp), następnie size(100dp) może przepełnić rodzica. Zawsze przemyśl kolejność świadomie, używając testów wyświetlania do weryfikacji wyniku.
| Kolejność | Wynik |
|---|---|
| padding → clickable | Kliknięcie działa również w obszarze odstępu |
| clickable → padding | Kliknięcie działa tylko na treści, odstęp to martwa strefa |
| size → padding | Element size(100), padding na zewnątrz → 100+2*pad |
| padding → size | padding zmniejsza miejsce, size może wyjść poza granice |
| background → padding | Tło wypełnia cały element wraz z obszarem zewnętrznym |
| padding → background | Tło tylko wewnątrz odstępu (obszar zewnętrzny przezroczysty) |
Standardowa biblioteka Compose zawiera ~50+ modyfikatorów podzielonych na kategorie. Rozmiar i pozycjonowanie: Modifier.size(), width(), height(), fillMaxSize(), fillMaxWidth(), fillMaxHeight(), defaultMinSize(), requiredSize(). Odstępy i granice: padding(), offset(), margin (realizowane przez padding rodzica lub Layout). Dekoracja: background(), border(), clip(), alpha(), shadow(), blur().
Zachowanie i gesty: clickable(), combinedClickable(), pointerInput(), draggable(), swipeable(). Rozmieszczenie w kontenerze: weight() (dla Row/Column), align(), alignBy(), matchParentSize(). Semantyka i dostępność: semantics(), testTag(), clearAndSetSemantics(). Rysowanie: drawBehind(), drawWithContent(), drawModifier() — modyfikatory umożliwiające własne rysowanie na kanwie.
Modyfikatory semantyczne — szczególna kategoria. Modifier.semantics {} określa, jak element będzie reprezentowany w drzewie Accessibility. Compose automatycznie wypełnia semantykę z tekstu, ale dla własnych komponentów trzeba ręcznie określać role, stany i działania. Jest to krytyczne dla zgodności z WCAG 2.2 i poprawnego działania TalkBack (Android) oraz VoiceOver (iOS).
@Composable
fun ModifierDemo() {
// Łańcuch modyfikatorów z poprawną kolejnością
Box(
modifier = Modifier
.size(150.dp)
.padding(8.dp)
.border(2.dp, Color.Gray)
.background(Color(0xFFE3F2FD))
.clickable { /* handle click */ }
.semantics {
contentDescription = "Demo card with click action"
role = Role.Button
}
) {
Text("Dotknij mnie")
}
}
Modifier.composed — to metoda fabryczna umożliwiająca tworzenie złożonych modyfikatorów, które mogą używać innych modyfikatorów, LocalComposition i lokalnego stanu. W przeciwieństwie do zwykłej funkcji rozszerzającej, composed tworzy instancję za każdym razem przy zastosowaniu, co pozwala na posiadanie własnego stanu wewnątrz modyfikatora.
Kiedy używać composed: powtarzające się kombinacje modyfikatorów (np. standardowy styl karty: padding + background + border + clickable); modyfikatory ze stanem (animowana zmiana tła przy naciśnięciu); dostęp do CompositionLocals (schemat kolorów MaterialTheme, gęstość pikseli). W zwykłych przypadkach wystarczy zwykła funkcja rozszerzająca bez composed.
Wydajność composed: każde wywołanie tworzy nowy obiekt modyfikatora, co może prowadzić do zbędnych alokacji przy rekompozycji. Aby temu zapobiec, opakowuj composed w remember. Google zaleca używanie composed tylko wtedy, gdy wewnątrz naprawdę potrzebny jest stan lub CompositionLocal. Dla statycznych kombinacji używaj zwykłych funkcji rozszerzających.
// Niestandardowy modyfikator przez composed ze stanem
fun Modifier.cardStyle(
elevation: Dp = 4.dp,
isSelected: Boolean = false
): Modifier = this.composed {
val backgroundColor = if (isSelected)
MaterialTheme.colorScheme.primaryContainer
else
MaterialTheme.colorScheme.surface
this
.fillMaxWidth()
.padding(12.dp)
.background(backgroundColor, RoundedCornerShape(8.dp))
.shadow(elevation, RoundedCornerShape(8.dp))
}
// Przykład użycia
@Composable
fun CardList() {
Column {
Box(Modifier.cardStyle()) { Text("Element 1") }
Box(Modifier.cardStyle(isSelected = true)) { Text("Wybrano") }
}
}
// Wersja statyczna (bez composed) — szybsza
fun Modifier.simpleCardStyle(): Modifier =
this.fillMaxWidth().padding(8.dp).clip(RoundedCornerShape(4.dp))
Unikaj odtwarzania Modifier przy każdej rekompozycji. Jeśli modyfikator nie zależy od zmiennych danych — wynieś go do stałej lub remember. Za każdym razem przy wywołaniu Modifier.padding().background() tworzone są nowe obiekty Modifier.Element. W izolowanym komponencie jest to niezauważalne, ale w LazyColumn z setkami elementów zbędne alokacje powodują zauważalne opóźnienia przy przewijaniu.
Zasada: jeśli łańcuch modyfikatorów nie zależy od parametrów funkcji Composable — zadeklaruj go jako val poza funkcją (na poziomie pliku lub Companion). Jeśli zależy — używaj remember(zależność) { ... }. Dla modyfikatorów, które są zawsze takie same, najefektywniejsze jest val poza Composable: takie obiekty tworzone są raz na cały czas życia aplikacji.
Modifier ordering best practices: rozmieszczaj modyfikatory w logicznej kolejności: najpierw rozmiar/odstępy (layout), potem dekoracja (background, border), następnie zachowanie (clickable, pointerInput). To nie tylko poprawia czytelność, ale także pomaga Compose Runtime optymalizować łańcuch na etapie pomiaru. Unikaj też zbędnych zagnieżdżonych Box z różnymi Modifier — często jeden Modifier na kontenerze nadrzędnym może zastąpić 2-3 zagnieżdżone.
// ✅ Dobrze: stała poza Composable
private val cardModifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.clip(RoundedCornerShape(8.dp))
@Composable
fun CardContent() {
Box(cardModifier.background(Color.White)) { ... }
}
// ❌ Źle: odtwarzanie przy każdej rekompozycji
@Composable
fun BadCard() {
Box(Modifier.fillMaxWidth().padding(16.dp)) { ... }
}
// ✅ Dobrze: remember dla dynamicznego Modifier
@Composable
fun DynamicCard(color: Color) {
val modifier = remember(color) {
Modifier.fillMaxWidth().background(color)
}
Box(modifier) { ... }
}
Często zadawane pytania
Tak, Modifier jest niezmienny, więc jeden obiekt można bezpiecznie używać w wielu miejscach. Jeśli jednak używasz composed-modyfikatora, każde wywołanie tworzy nową instancję. Dla statycznych łańcuchów stała lub val poza Composable to optymalne rozwiązanie.
Użyj Layout Inspector w Android Studio — wizualnie pokazuje granice każdego Modifier. Do programowego debugowania dodaj Modifier.border() z różnymi kolorami na każdym etapie łańcucha, aby zobaczyć granice zastosowania każdego modyfikatora.
Modifier.then(other) dołącza łańcuch other do this. Sekwencyjne wywołanie (Modifier.a().b()) jest równoważne Modifier.then(a()).then(b()). Nie ma różnicy — to ten sam mechanizm łańcucha. then() jest przydatny, gdy trzeba dołączyć gotowy łańcuch z zmiennej.
Modifier.semantics {} określa, jak element będzie opisany dla czytnika ekranu. Modifier.clickable() automatycznie dodaje rolę Button i Action(OnClick). Dla własnych gestów trzeba jawnie określić semantics. Bez modyfikatorów semantycznych użytkownicy TalkBack nie będą mogli wchodzić w interakcję z własnymi komponentami.
Modifier.background(color, shape) działa z rogami, ale clip() musi iść PRZED background, aby rogi zostały przycięte. Prawidłowa kolejność: clip(shape).background(color). Jeśli trzeba przyciąć również treść wewnątrz, użyj clipToBounds() na rodzicu.
Podsumowanie
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.
Przeczytaj również