Otevře se na nové kartě

Jetpack Compose

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.

Zpět na hlavní stránku