Jetpack Compose je hlavní UI toolkit, který používám pro moderní nativní Android aplikace.
Umožňuje vytvářet rozhraní deklarativně na základě aplikačního stavu namísto ruční manipulace s jednotlivými View prvky. Díky tomu je UI kód srozumitelnější, znovupoužitelnější a výrazně lépe zapadá do moderní Android architektury založené na Kotlinu.
Jetpack Compose používám společně s technologiemi, jako jsou ViewModel, Kotlin Flow, Room, DataStore a Material 3, k vytváření aplikací, které jsou responzivní, udržovatelné a postavené na jasně definovaném vlastnictví stavu.
Pro mě Compose není jen náhrada za XML layouty.
Mění způsob, jakým je samotná UI vrstva strukturovaná.
Jak používám Jetpack Compose
Jetpack Compose používám například pro:
- kompletní rozhraní Android aplikací,
- responzivní layouty obrazovek,
- Material 3 design,
- znovupoužitelné UI komponenty,
- formuláře a obrazovky nastavení,
- seznamy a gridy,
- navigaci,
- dialogy a bottom sheety,
- animace,
- adaptivní layouty,
- rozhraní řízená stavem,
- stavy načítání a chyb,
- interakci s API platformy Android.
U nových Android projektů obecně preferuji Compose, protože přirozeně zapadá do Kotlinu a moderní Android architektury.
Deklarativní UI
Tradiční vývoj UI často znamená vytvořit View a následně ručně měnit jeho jednotlivé vlastnosti podle změn aplikačního stavu.
Compose tento vztah obrací.
Aplikace popisuje, jak má rozhraní vypadat pro aktuální stav.
Koncepčně:
stav
→ composable funkce
→ UI
Pokud se stav změní, Compose aktualizuje dotčené části rozhraní.
Není potřeba ručně vyhledávat jednotlivé View prvky a synchronizovat je s aplikačními daty.
Vzniká tak výrazně jasnější vztah mezi aplikačním stavem a vizuálním výstupem.
Architektura řízená stavem
Stav je v Compose zásadní.
Preferuji rozhraní, kde lze viditelnou obrazovku odvodit z explicitního stavu místo skrytých mutací rozptýlených napříč UI.
Stav obrazovky může obsahovat například hodnoty:
loading
data
error
selectedItem
dialogVisible
userPreferences
Composable podle tohoto stavu vykreslí odpovídající UI.
Akce uživatele se posílají zpět jako události.
Vzniká tak předvídatelný tok:
Stav
↓
UI
↓
Akce uživatele
↓
ViewModel
↓
Nový stav
Tento pattern výrazně usnadňuje pochopení složitějších obrazovek.
State hoisting
State hoisting používám k tomu, aby znovupoužitelné komponenty nebyly závislé na tom, odkud jejich stav pochází.
Místo toho, aby komponenta spravovala důležitý aplikační stav interně, předávám jí aktuální hodnotu a callbacky.
Například:
SettingsSwitch(
checked = state.notificationsEnabled,
onCheckedChange = onNotificationsChanged
)
Komponenta tak ví pouze:
- co má zobrazit,
- jakou událost má vyvolat.
Nemusí vědět, zda hodnota ve výsledku pochází z DataStore, Room nebo vzdáleného backendu.
Tím se zlepšuje znovupoužitelnost i testovatelnost.
Integrace s ViewModelem
ViewModel běžně používám jako vlastníka aplikačního stavu na úrovni obrazovky.
ViewModel může:
- zpřístupňovat UI stav,
- zpracovávat akce uživatele,
- komunikovat s repository vrstvou,
- spouštět asynchronní operace,
- přežít konfigurační změny.
Compose následně pozoruje stav vytvářený ViewModelem.
Vyhýbám se umisťování business logiky přímo do composable funkcí.
UI by se mělo soustředit na prezentaci a interakci.
Kotlin Flow
Compose přirozeně funguje s Kotlin Flow.
Flow používám ke zpřístupnění reaktivních dat ze zdrojů, jako jsou:
- Room,
- DataStore,
- repository vrstvy,
- aplikační služby.
Typická architektura může vypadat takto:
Room / DataStore / API
↓
Repository
↓
Flow
↓
ViewModel
↓
Compose
Když se podkladová data změní, nový stav postupuje aplikací a UI na něj automaticky reaguje.
Recomposition
Compose aktualizuje UI prostřednictvím recomposition.
Composable funkce navrhuji tak, aby recomposition zůstala levná a předvídatelná.
To znamená vyhýbat se zbytečné práci přímo uvnitř composable funkcí.
Nákladné operace by se neměly opakovat jen proto, že komponenta potřebuje znovu vykreslit.
Takovou práci přesouvám do:
- ViewModelů,
- repository vrstev,
- zapamatovaného stavu,
- derived state,
- zpracování na pozadí.
Porozumění recomposition je důležité jak pro výkon, tak pro architekturu.
Stabilní stav
U složitějších rozhraní věnuji pozornost stabilitě dat předávaných do composable funkcí.
Zbytečné vytváření nových objektů nebo nestabilní stavové struktury mohou způsobovat více recomposition, než je potřeba.
Kde je to vhodné, preferuji předvídatelné immutable UI modely.
Tím se usnadňuje pochopení změn stavu a může se zlepšit efektivita vykreslování.
remember
remember používám pro hodnoty, které patří k aktuální composition a nemusí přežít znovuvytvoření procesu.
Typickými příklady jsou:
- dočasný UI stav,
- stav animace,
- lokální stav interakce.
Odlišuji to od aplikačního stavu, který by měl být ve ViewModelu nebo v perzistentním úložišti.
Ne každý stav patří na stejné místo.
rememberSaveable
Pro menší části UI stavu, které mají přežít znovuvytvoření Activity, může být užitečné rememberSaveable.
Příklady mohou zahrnovat:
- vybrané záložky,
- zadané hodnoty formuláře,
- rozbalené sekce UI.
Pro důležitější aplikační stav stále preferuji ViewModel nebo perzistentní úložiště.
Správný vlastník stavu závisí na tom, jak dlouho musí daná informace přežít.
Side effects
Composable funkce by ideálně měly zůstávat deklarativní.
Když rozhraní potřebuje komunikovat s okolním světem, používám Compose side-effect API záměrně.
Příkladem může být:
- spouštění jednorázových operací,
- reakce na změny stavu,
- registrace a odstraňování listenerů,
- aktualizace externích objektů.
Cílem je udržet imperativní chování pod kontrolou místo toho, aby během libovolné recomposition docházelo ke skrytým side effects.
LaunchedEffect
LaunchedEffect používám tam, kde je potřeba spustit coroutine v reakci na definovanou změnu lifecycle Compose nebo změnu key.
Může být užitečný například pro:
- reakci na stav,
- spuštění počáteční operace,
- zobrazení událostí,
- koordinaci navigace.
Keys udržuji explicitní, aby se efekt spouštěl pouze tehdy, kdy má.
DisposableEffect
Když Compose potřebuje registrovat zdroj, který je později nutné uvolnit, poskytuje DisposableEffect jasnou hranici životního cyklu.
To může být užitečné při práci s:
- listenery,
- observery,
- externími API platformy.
Cleanup je důležitý.
Lifecycle-aware UI by nemělo po opuštění příslušné části composition dál držet nepotřebné prostředky.
Znovupoužitelné komponenty
Jednou z hlavních výhod Compose je snadnost vytváření znovupoužitelných UI komponent.
Composable funkce vytvářím pro opakující se patterny rozhraní, například:
- karty,
- řádky nastavení,
- tlačítka,
- informační panely,
- indikátory stavu,
- formuláře,
- dialogy.
Preferuji komponenty, které dostávají explicitní stav a události místo závislosti na skrytém globálním stavu.
Díky tomu je lze snadněji používat v různých kontextech.
Material 3
Material 3 používám jako designový základ mnoha Android aplikací.
Poskytuje zavedené komponenty a design patterny pro:
- tlačítka,
- karty,
- navigaci,
- dialogy,
- typografii,
- barevná schémata,
- surfaces.
Vizuální identitu aplikace ale stále přizpůsobuji konkrétnímu produktu.
Material poskytuje základ interakcí a komponent.
Výsledný design by měl stále odpovídat účelu a charakteru produktu.
Design systémy
U větších aplikací definuji znovupoužitelné design tokeny a komponenty místo samostatného stylování každé obrazovky.
Může jít například o:
- rozestupy,
- typografii,
- zaoblení rohů,
- velikosti komponent,
- vlastní komponenty.
Konzistentní design systém působí profesionálněji a výrazně usnadňuje pozdější vizuální změny.
Tmavý režim
Compose výrazně usnadňuje podporu různých barevných schémat.
Témata navrhuji tak, aby aplikace mohly podporovat:
- světlý režim,
- tmavý režim,
- systémové nastavení.
Kde je to možné, vyhýbám se hard-coded barvám v jednotlivých obrazovkách.
Komponenty by měly používat sémantické barvy tématu, aby se rozhraní přizpůsobovalo konzistentně.
Dynamic Color
Kde je to vhodné, může Android poskytovat dynamická barevná schémata odvozená ze systému.
Dynamic Color vnímám jako volitelné vylepšení, ne jako něco, co musí používat každá aplikace.
Správná volba závisí na brandingu a roli konkrétní aplikace.
Responzivní layouty
Android aplikace běží na velmi široké škále obrazovek.
Compose rozhraní proto nenavrhuji pouze pro jeden pevný rozměr telefonu.
Layouty by se měly přizpůsobit:
- různým šířkám,
- režimu na šířku,
- tabletům,
- skládacím zařízením,
- škálování písma z důvodu přístupnosti.
Místo zbytečného spoléhání na pevné rozměry používám responzivní a adaptivní layout strategie.
Adaptivní UI
U aplikací, které mohou běžet na větších obrazovkách, zvažuji, zda by se navigace a informační hierarchie neměly změnit, místo aby se celé rozhraní pouze roztáhlo.
Například:
malá obrazovka
→ spodní navigace
větší obrazovka
→ navigation rail
nebo:
telefon
→ jednostranný list/detail
tablet
→ seznam a detail vedle sebe
Adaptivní design by měl dostupný prostor využívat ke zlepšení použitelnosti.
Seznamy
Pro rozsáhlé nebo dynamické kolekce používám lazy komponenty Compose, například:
- LazyColumn,
- LazyRow,
- lazy gridy.
Tyto komponenty vykreslují pouze položky potřebné pro aktuálně viditelnou oblast místo vytváření všech položek najednou.
To je zásadní pro výkon u seznamů s velkým množstvím záznamů.
Stabilní klíče
U dynamických seznamů používám tam, kde je to vhodné, stabilní klíče.
Pomáhají Compose identifikovat položky, když:
- se seznam mění,
- položky mění pozici,
- se položky přidávají nebo odebírají.
Stabilní identita může zlepšit správnost i chování animací.
Navigace
Navigation Compose používám k definování navigace mezi obrazovkami v Compose aplikacích.
Routes by měly jasně reprezentovat cíle aplikace.
Vyhýbám se používání samotné navigace jako místa pro ukládání velkého množství aplikačních dat.
Místo toho preferuji předávání stabilních identifikátorů a nechávám cílovou obrazovku načíst data přes příslušnou stavovou vrstvu.
Formuláře
Compose je vhodný pro tvorbu formulářů řízených stavem.
Stav formuláře udržuji explicitní a oddělený:
- aktuální hodnoty,
- validační chyby,
- stav odesílání.
UI by mělo jasně komunikovat:
- povinná pole,
- neplatné hodnoty,
- načítání,
- úspěch,
- selhání.
U složitějších formulářů se vyhýbám tomu, aby se každé vstupní pole stalo samostatným zdrojem aplikační pravdy.
Validace
Rozlišuji mezi:
- okamžitou validací v UI,
- aplikační validací,
- validací na backendu.
UI může poskytovat rychlou zpětnou vazbu.
Aplikační vrstva může vynucovat business pravidla.
Backend zůstává zodpovědný za validaci všech externě odeslaných dat.
Compose řeší prezentaci, ne konečnou důvěryhodnost dat.
Stavy načítání
Aplikace často musí čekat na:
- síťové požadavky,
- databázové operace,
- billing,
- zpracování na pozadí.
Načítání reprezentuji explicitně.
Uživatel by měl rozumět tomu, že aplikace pracuje, a neměl by neaktivní obrazovku považovat za selhání.
Podle typu operace to může znamenat:
- indikátory průběhu,
- skeleton stavy,
- deaktivované ovládací prvky,
- informace o průběhu.
Chybové stavy
Chyby jsou běžnou součástí aplikačního stavu.
Compose obrazovky navrhuji tak, aby selhání zobrazovaly jasně.
Může jít například o:
- nedostupnou síť,
- neplatná data,
- zamítnuté oprávnění,
- selhání operace,
- prázdný výsledek.
Dobrý chybový stav by měl vysvětlit, co se stalo, a pokud je to možné, nabídnout jasný způsob nápravy.
Prázdné stavy
Obrazovka bez záznamů nemusí nutně znamenat chybu.
Záměrné empty states používám k vysvětlení:
- k čemu obrazovka slouží,
- proč se momentálně nic nezobrazuje,
- jakou akci může uživatel provést.
To je důležité zejména u nových instalací, kde jsou prázdné databáze běžným stavem.
Animace
Compose nabízí velmi schopný animační systém.
Animace používám tam, kde zlepšují:
- kontinuitu,
- orientaci,
- zpětnou vazbu,
- vnímanou kvalitu.
Nepřidávám pohyb jen proto, že to framework umožňuje snadno.
Animace by měla podporovat pochopení toho, co se v rozhraní změnilo.
Přístupnost
Přístupnost je součástí implementace UI.
Zohledňuji:
- content descriptions,
- sémantické role,
- velikost dotykových cílů,
- kontrast,
- škálovatelný text,
- ovládání klávesnicí nebo alternativním vstupem tam, kde je relevantní.
Vlastní composable komponenty by neměly ztrácet sémantické informace, které by standardní Android komponenty jinak poskytovaly.
Škálování písma
Uživatelé mohou výrazně zvýšit systémovou velikost písma.
Layouty proto navrhuji tak, aby je větší text okamžitě nerozbil.
To znamená vyhýbat se předpokladům typu:
- každý label se vejde na jeden řádek,
- každý ovládací prvek má pevnou výšku,
- rozměry textu se nikdy nezmění.
Podpora škálování písma zlepšuje přístupnost i celkovou robustnost layoutu.
Dotykové cíle
Interaktivní prvky potřebují dostatečný prostor pro spolehlivé použití.
Vyhýbám se vizuálně malým ovládacím prvkům, které vyžadují téměř pixelově přesný dotyk.
I když je viditelná komponenta kompaktní, skutečná interaktivní oblast může stále poskytovat pohodlně velký touch target.
API platformy
Compose aplikace stále potřebují komunikovat se samotnou platformou Android.
Při práci s:
- oprávněními,
- activity results,
- lifecycle,
- systémovými službami,
- externími intents
používám vhodné integrační patterny Compose.
Interakci s platformou se snažím izolovat místo rozptylování závislostí na Android Contextu napříč UI kódem.
AndroidView interoperabilita
Ne každá Android knihovna poskytuje nativní Compose komponentu.
Kde je to potřeba, integruji tradiční View komponenty prostřednictvím interoperability API.
To může být užitečné například pro:
- starší SDK,
- reklamní komponenty,
- specializované platformní View prvky.
Tyto hranice udržuji malé, aby zbytek aplikace mohl zůstat plně založený na Compose.
Integrace legacy View systému
Compose lze postupně zavádět také do existující aplikace založené na View systému.
To umožňuje migraci bez nutnosti přepisovat celé rozhraní v jediném kroku.
U existujících projektů tak mohu zvolit postupný přístup místo vytváření zbytečného rizika kompletním přepisem UI.
Výkon
Compose dokáže vytvářet velmi výkonná rozhraní, stále ale záleží na architektuře.
Věnuji pozornost:
- zbytečné recomposition,
- nákladným operacím v composable funkcích,
- nestabilním parametrům,
- velkým seznamům,
- nadměrným změnám stavu,
- načítání obrázků,
- nákladům animací.
Optimalizuji podle skutečného chování aplikace místo snahy ručně zabránit každé recomposition.
Největší přínos obvykle přináší správně navržený stav.
Práce na hlavním vlákně
Vykreslování UI probíhá na hlavním vlákně.
Vyhýbám se provádění náročných úloh přímo z composable funkcí.
Operace jako:
- velké databázové dotazy,
- zpracování souborů,
- síťové požadavky,
- složité výpočty
patří do vhodných vrstev běžících na pozadí.
UI by mělo dostávat výsledek, ne provádět samotnou práci.
Integrace Room
Room používám s Compose tam, kde obrazovky potřebují reaktivní relační data.
Typický tok je:
Room
→ Flow
→ ViewModel
→ Compose
Změny databáze tak mohou automaticky aktualizovat rozhraní.
Composable nepotřebuje přímý přístup k databázi.
Integrace DataStore
Pro preference DataStore přirozeně zapadá do stejné architektury.
Například:
DataStore
→ Flow
→ Settings ViewModel
→ Compose
Změna nastavení může aktualizovat perzistentní úložiště a nová hodnota se následně automaticky propaguje rozhraním.
Integrace REST API
U vzdálených dat obvykle držím síťovou komunikaci za repository vrstvou nebo službami.
UI dostává stavovou reprezentaci, například:
Loading
Success(data)
Error
Compose pro každý stav vykreslí odpovídající obrazovku.
Tím se zabraňuje pronikání implementačních detailů síťové vrstvy do UI.
Google Play Billing
Billing integrace obsahují imperativní operace SDK, zatímco Compose je deklarativní.
Billing logiku proto držím v samostatné komponentě nebo ViewModelu a do UI zpřístupňuji pouze výsledný stav.
Compose může vykreslovat například:
- dostupný produkt,
- tlačítko nákupu,
- premium stav,
- načítání,
- chyby.
Billing SDK zůstává mimo composable vrstvu.
Google Mobile Ads
Reklamní integrace často vyžadují komunikaci s tradičními komponentami Android SDK.
Tuto logiku izoluji místo toho, abych kód pro správu reklam rozmisťoval napříč Compose obrazovkami.
UI by mělo vědět, zda reklama potřebuje prostor nebo zda jsou reklamy deaktivované.
Nemělo by ale odpovídat za celý životní cyklus reklamního SDK.
Testování
Composable funkce s explicitními parametry lze snadno testovat izolovaně.
Mohu testovat:
- zda se zobrazuje očekávaný obsah,
- chování interakcí,
- změny stavu,
- spouštění navigace.
Udržování composable funkcí nezávislých na globálním aplikačním stavu zlepšuje testovatelnost i previews.
Previews
Compose previews jsou užitečné během vývoje UI.
Umožňují vykreslit komponenty a obrazovky s předdefinovanými ukázkovými stavy bez spuštění celé aplikace.
Previews používám ke kontrole:
- běžných stavů,
- prázdných stavů,
- stavů načítání,
- chybových stavů,
- různých témat.
Preview nenahrazuje testování na zařízení, ale může výrazně urychlit iteraci rozhraní.
Udržovatelnost
Compose funguje nejlépe, pokud projekt zůstává modulární.
Vyhýbám se vytváření jednoho obrovského composable, který obsahuje:
- přístup k datům,
- navigaci,
- business logiku,
- volání platformy,
- vykreslování.
Místo toho odděluji:
- stav obrazovky,
- události,
- znovupoužitelné komponenty,
- aplikační logiku.
Jednotlivé soubory tak zůstávají srozumitelné i s růstem aplikace.
Vyhýbání se nadměrné abstrakci
Compose usnadňuje vytváření malých komponent, přílišná fragmentace ale může rozhraní naopak znepřehlednit.
Komponenty vyčleňuji tehdy, když přinášejí:
- znovupoužitelnost,
- jasnější odpovědnosti,
- snazší testování,
- smysluplné oddělení.
Nevytvářím samostatnou abstrakci pro každých několik řádků UI.
Architektura by měla zlepšovat čitelnost, ne maximalizovat počet souborů.
Jetpack Compose v mém technologickém stacku
Jetpack Compose běžně používám společně s technologiemi, jako jsou:
- Kotlin,
- Android Jetpack,
- ViewModel,
- Kotlin Coroutines,
- Kotlin Flow,
- Room,
- DataStore,
- REST API,
- Google Play Billing,
- Google Mobile Ads / AdMob,
- Material 3.
Compose poskytuje prezentační vrstvu, zatímco zbytek architektury dodává stav, perzistenci, síťovou komunikaci a funkcionalitu platformy.
Proč používám Jetpack Compose
Jetpack Compose používám proto, že nabízí moderní způsob tvorby nativních Android rozhraní řízených stavem.
Jeho největší výhodou není pouze menší množství UI kódu.
Vytváří výrazně jasnější vztah mezi aplikačním stavem a tím, co uživatel vidí.
V kombinaci s Kotlinem, ViewModelem, Flow, Room a DataStore mi Compose umožňuje vytvářet Android aplikace, ve kterých rozhraní zůstává reaktivní, modulární a udržovatelné i s růstem projektu.
Právě proto patří Jetpack Compose mezi centrální technologie mého Android vývojového stacku.