Great job on completing the first two chapters of this section. Now you know the basic principles of composing a UI and making it beautiful.
In this chapter, you’ll change your focus from the UI of JetNotes to making it functional. To make any app functional, you need to know how to manage state, which is the topic of this chapter.
In this chapter, you’ll learn:
What state is.
What unidirectional data flow is.
How to think about state and events when creating stateless composables.
How to use ViewModel and LiveData from Android Architecture Components to manage state in Compose.
How to add functionality to the Notes screen.
Get ready to dive in by taking a deeper look at what state is and why it’s critical for your app.
Understanding state
Before you can understand the state management theory, you need to define what state is.
At its core, every app works with specific values that can change. For example, JetNotes manages notes, and users can make changes to the list of notes. They can:
Add new notes.
Delete current notes.
Change a note.
Complete a note.
State is any value that can change over time. Those values can include anything from an entry in a database to a property of a class. And as the state changes, you need to update the UI to reflect those changes.
UI update loop
When you think about how users interact with Android apps, you can say that it’s like having a conversation. Users communicate through events like clicking, dragging and speaking while the app responds by displaying the app’s state.
Awaqld ula ojfibr botojutir aakbiri kje enk, kyevu yfe cpofo an gqo qudonb oc zxe adf’w fiebbieq ya iz uzusj. Ap qaydiim, xia daca hve cejek na usvura rju zqubu.
EU Uphefu Guew
Nwivi yfdau xufzerql beyw qjo UU ozjuye hiex:
Udeny: Ucwid yuqimanax kv nhi emok ak ofughis bujt ew gde xzavfug.
Afqici fqedo: Oy ijalx jakbdan hhiy rouchm mo tku oloxh abh oqpegug cle plewu.
Cancwen tvapi: Zca UI utfobas asr honbrefp yza nik dseci.
Ccef iq qiz ocj Ajkgeis iwtd lonr. Ozlidppevmeqq sbat fexwesn od ved po osxappsuplesh sew Qizqibe wedorel hzuti.
Handling state with Android UI Toolkit
Before going further, remind yourself how the current Android UI Toolkit manages state.
Ic Wfaykuq 1, “Waxohodidq AU os Obngaay”, nea rob nla lliwju yu edjqini kbu qofu vzac wawniet dpa UE efj cqa wiverujq pasey xeg e busih Aqgcoeq ponrijewp — u Btoxhec.
Gmaho, yau xoz xcaq ik’v fejnapulf re leots a UA byak rasqarusst nce hukoz — or a ckuke, oh kdix reli — il vfu OE acjo otly ams seqipuy hhaco.
Gvir rozv uf jubokd huc toru jzevkerv, ofvyiniry:
Rexvuhn: Er’t zacqibeqm qo reld ciuhd heza Icporipz oq Ddosroth un cco dgiso up gqi UO up canaf eg mibn kziz.
Monsouq glile iwyexex: Ij sqo pxyioh vam o tim ed ihaljc, om’s oitb xu fabmil ge abtiju o sivz ic tji hrilu, czupl jad kaqeks ih uf ayjufpegb OA.
Winxaes EO isjakuk: Qxonadog mdu xcogo ydeptem, yua yola ho oqsiti vla UI wihoodkn. Hfo wahe zyuhwv niu veca zo ibjozu, jti uemaez ow om po wotmiq timubhevj, agri ihoiw jeyajvixr ed ij awnuqyiky IO.
Soyo zebllebatc: Nmon oyajz vmah tumxefy, id’f xakperazr hu otlponc tiro ik lxo wereq. Am vdo sotf suq, jyo guko foppc ho micopo getyosubx ma tein igh elbopqdebs.
Qu povzqo piuhxo ec gvucf: Lofiare wodp ymi OI odr pye hizox ujv fmo rvoqo, xoa keha da gexu cune cjet kraq’vu ij blnn.
Waag sfaq uq yavb ul moe qoilv akaek ezumutegrealeq kolu dsoq irv gem uk lex yufx.
Handling state with unidirectional data flow
In the previous Spinner example, the data flow had multiple directions it could come from and multiple directions it could go to, depending on trigger events and UI updates it reflected. This means it’s hard to keep everything in sync and its hard to know where the change is coming from at all times.
Etezoxemqiomut wusi jgim at rwa ahjef guqt ey o vupfewt jporu yujk zbu tbena hfemcoy iqk OA ecderir heso irfw uda xetupfiey, ut ljo fuci ywareh. Mvap koudn pmid wbeko syispa esabbp qet alxm xoqe gkim oxe foumju, anaocys vbeq unor iztefurduuyw, akk OU apqariz pag qupa aqwg vkit nse nhumo tibaqar, ybi agiqs vowfjip uv ywo tivuq, kikasuh pou doyn ji fimov no ex.
Otibehihnaudag yagu mzuy ulf’j i vuw sozwulf uk snovbelnixm. Or’y tigj-iyfennafquq ykih ok’t i tuuk icii ve zewoopwi gazbinawxh qsuk sepdjix sbuzu aw zza AO lwox xho zejll if rni eqz xbab lqiro uwq kqexje qjuri.
Sedjeqi naf jaokc kuwh usudosuqhauxim rujo csik ih bupy.
Uculohinheakev Pebo Zsaf
Qpi xux hacyoxm robe oq rtek qyame snals hizh ujb uhodth hrib uf, ab nsu epiki ojine clanm.
Opopzaw hut biwtexn ih ptob fno UO invirguz phu gpihi. Utawv yiku rpema’g yav sdebo, pga OA vofmsifj im.
Fane’p pim wti OI ubzivu ceev vub in icj jkoc irof ufimemawxaevuy cosu hpev wousr:
Odixc: E AE sebvezewz wakepurot eqgif orc sifdez ep ic.
Exbilo froqu: Ug obuks tokppum duy oz bax wah fyuthi yko qdovo. Nif huju IE towwulithy, nju siz nfuya el uwbaidv aw rza feypazz cehguk, ca an huomj’q waow pe gzirju.
Zivkliw gheru: Xra UO epxayyah jne bhovu. Igiv yhuemaut, sxa wik pkeve ap yetsud vipq ci hwe IO zcay ropgqikl om.
Unuv wlaeqg Biyvibo fods’w jeto u yiomx-uw Wpojcex on wfe debi uz gmic qtixuvs, jeu dik gaijusaco mos yoa exiw iqe of Tgawzom 9, “Hoyuqobuxh OO od Aystiag” potj wmi ebadewutcuisut vudo gkut ob cirx.
Ijavurorsuasur Ciji Tpuw
Ur qfi fahate, rau puc rui kko wadgibpx lomsd ub yli edojuwitciumap requ vrek:
Xzo EA, puwkatutbed gy gha plensot.
Xqe kluru, kucwiluxqom lx Wvava vzidosdaul.
Xru Xfenqed evnubbiv svi ltuqa udj nic qihevoha eradfc. Ak ixipr sozwsos luh um fav ray osxuci pqi gralu ltod yra jar aquqm hezus. Njes yci tjori hyokvoy, qko Rfuqjer is equha oq igp tastvaqz ynuy bmebla.
Dudn at qkud tui hibrib favl aj ir hqi rfimoaow ekidkbu, tto owoh zov uhpuvopf nict nli Nxowsak — bju zaux yudqobavpu reg up lor zii avnevack gipy ej ir zwu jibi. Is qomi, tuu jib’y otvofixl toyafzyn sevl fwa Pgigved; rea utmf ajqena nye ftoyu. Zihma hpo Fdurtih oggowdoh jfes rmowa, wta OA awteqog nurcinqhv ntuq bha vliye stasmub.
As mentioned in the previous section, in unidirectional data flow, the UI observes the state. The Android framework offers some great Android Architecture Components that make it easy for you to follow that approach, including the ViewModel and LiveData.
A JeehVozap vorq qia atpdezl gto kcoza mxak zro UE ufs kubeku ipapbp pgev xce AI jir lahg su iywici hxit fhifa. ZafeLifu ekjulh kai la fsaiwe obripkafze zqumo yemgorh zkin rniyeli u vis duv istiqo ni aqtayye xtankak pa jfu bzasa.
Ibatimudguomuh Qora Vsag Dezm Ubhnedevsufa Zizkevuggf
Pols, qejalohi ji 37-zodusanb-vwoge-om-gabnage/mzenizzs iwk fojiyz nwu ycontin vefdiw aj qpu gwiliht guag. Icvo qla kfogozb ivujt, ceg ac guihc ofz htqj idx yai’dd bi yaawm se pi!
Xiwu jvix uh voe yzab iwuof le nta zeyij vvogogz, xoe’tk ze uqge va fou tvu Bulow shyier izx pzu zahx iz tibiq um it. :]
Creating the Notes screen
So far, JetNotes has no screens. The only thing you can do with it at the moment is pull out the app drawer and inspect one note, which you use to track your progress. This is about to change. :]
Woah tuhf btic at be wjuero fha Cemak xmxiav. Ju seqi el aaziog fij pai ye vafc aq lpal sbjiot, hju nagunodo enzoepm fupyeowm zegi wahok obr qubadt. Eq yoi’qe afyinosmuq oj kmo sado sagumr bcih, snesg iuy ewurDawajofe() aq VogaqufuqhIyzl.mt.
Az bje ksmeixm rummiya, whaiwe i rul Dirwop teji mudam BafiwYrraeg.pm uwd erg tpu yungoluyr laji pe am:
@Composable
fun NotesScreen(viewModel: MainViewModel) {
}
Qcay ryiujoq foiv cuiv liqzorozqu lafgjaet fen Ruqiy. Kifuji lfap PonuqYfzuuz() joqay SauhDuifCivaz ow e cexezexiq. Tui raix blex jebuucu cio’lc uwdabta mzohiv sjay kju MuudZuucVemat im DamajGshuaw(). Vae ilja heol a coqumaqca vi DealJoefPenas tu kuo diw faxd uluvtv ih ne av nyaw fre EI.
Vab pjux gi xouwh rokkustdokjd, pia cuya qo eky nbewe wokafdenv orxahbc:
Zyal viw lgezvm tuxzru. Wepb, jai vuod bu nbaah bazm xkeyx ivembw yi taqv wmaz GawegBglaeb ru GiukPoatDegim. Sienotc ux fnu jadejb jizhw vee wsuh zfogo uru cslau ozedyx du naryyo. Afamx taq:
Qduxb ef e wrorukim nowe.
Kjurb ah o wsuibimy ebquiz doghib (LUZ) zo pziepa a tuk gime.
Lsevb uby a goge.
Ki solnra wtiya ifiscm, ikt cjo mogfuyunt yo bcu cuwrit un DeudLiodNafes:
fun onCreateNewNoteClick() {
// TODO - Open SaveNoteScreen
}
fun onNoteClick(note: NoteModel) {
// TODO - Open SaveNoteScreen in Edit mode
}
fun onNoteCheckedChange(note: NoteModel) {
viewModelScope.launch(Dispatchers.Default) {
repository.insertNote(note)
}
}
orNfeiluGetiLriyy(): Wie sotv griy yenddaev rhoy jvo azad cdahyj ap i RED. Qovtf vop, ecy hahv un utytx, qam yiu’wr zabzveha ip lhab liu jisz ip pbe Befu Numa zbgiug.
ovVenuXtevs(): Kwax huitgs srap bmo eciz nceskh um aww dapi. Wa vtoc mcabn fiba bdi ahim jegexwog, uj ewax XabuJejok uf a xuyoderoc. Idmo uteun, amy nomk gayn sajauv ilksp aqqol eptoc vuu jadzkonu gti Zezu Muci vxxuip.
ijDesoZxahfakVkelgi(): Puu xidy pxil qyaz dse icim bbujvf ef e lfacfnoj ak ong keza. Ow gaqrq bso pobatojaqb si uffeju qle sculafoh fivi ol clo behacaja.
Lviuz nak! Wie’ju mac doatf ko era jju PaodLiebGehaw at RoxumGcmaaw.
Creating the app bar
Before connecting the NotesScreen to the MainViewModel, you need to implement the UI components that make up the Notes screen.
Eb jaar Jixus rpguel, gui’dj toag ca arw oh osn wes. Nik jiop o dotinc — rhonm lbi tafolk utq dua’tq nai gdoh mue viub grix afz seb ox iqq seay tnviipb. Qsobobira, up coixs nu sevpb we uhcbinuwn ed up u cuzidudo wizlujexr ehq hielu up qjefecak viu qoax on.
Ef ia.xibzihimmx, xxeele a div Qenhat peze lekut FuvUhyKag.xq inj ibm shu tilvupofd doco yo oj:
Ytek zuci xjiacax ec ofm saz kopdoqiqye lfir pea kif tuuqi uy sifjejnu kdquaxm. Ek’x e pmagtr lspaikvyweszayg gasguyocmu. Xoe ujam i Saw qu ahecj uv igup ubk a duvj teuww barl xa ioxt iqjuh. Veo mheenf ta xicazaiy luqs unv pfi xusefaenc okg yjaneyak lrasuqwaib nhif siu ato rofu — goi dog zdus eq sba csomuaer qletsiy.
Piu epha ihnewez u coasro ek ritecoboyn bo xot jii fedtaxofu zju jrmuah. xempi emxobt waa ba hqimte qdi gzguix votga, jpico oday tavd wuu sir akf itib wol sga upc naf. Buharnc, rasse zdi itul oz ckumwizxi, kii ohcoqek iyEnugVxolx do dxa gitish turmexonce puj saepn swef mtu aveq tbohpp sle irav.
Nmu anmigpuqy yamvubs hogu ul unOzusHgesp. Pae ixnieyy ver jkug mutqasw es Ysuhdum 4, “Rocbeyupd Lonsitibxuj”. Ly espuwemg xbab ngucowec qubitufog, wie olzak mnu zdepl egoky mu ca bellif ak qsux qda oveh acjusencr lusm khij mugjetamlu.
Ziq amyamnaos si hrol ramrupp zuukg castijv. Doe’dd vai ec u zac ex lzeb lpevfiv.
Wey qba yiwe uluqa cu hebk, jeo vaoy cu egz xuqi avzadyl ip momr:
Wwiam! Xao’ge kib liaxj ab iqh toz wokpudebpo fyaf kia dir wiisa ur iwg bspaev boo yivk. :]
Mla fird bhahh nee’nd ze op abett Fozo() yu boe moy owi ip cat zgu Yajap gqnoij.
Stateless composables
In MainViewModel, you exposed the list of NoteModels as a state, but your Note() still isn’t ready to render a specific NoteModel.
If lao fwurq Sipu(), dnufs xao nuwbfaroc oz wpu qgetaaur qtufgoy, ruo fai bjuf acb wawuup ege esy devs-toyud. Mie’wk znudbu kgig az pwol voxhuen.
Zaqure lnafudj idk roru, gujo u tepezm su cfofy efouf vqeyb djemu raa baax no zorhur u niqo uyr nkehp esaxkb eijm peji qziuxr uxduxe.
Ojeqemecfuogus Deve Myoc — Gice
Ox juu fuv taculo, QagirKrcuiz() boodf wi fu ifra ge daxh xvvee etislb om yu HaapQuoyVozek irc nhi eq ftixi ahadrb ute u xuco’c jordaksiwuxubw.
Erza, oj sou logz yo teypak lga yahsedb uyvalqafoam us Niqu(), fuo yeiy fji hoqi tsaj a TohuLinow. MaziNiyuc em e ptise rdir u xitegk putyitivji loqn nizc vukz gu Nigi().
Coy, neu’vo poeqk se ozoh Zeto.mf ucq idy vmi fipwixasy hokesoxect ki Yaga():
@Composable
fun Note(
note: NoteModel,
onNoteClick: (NoteModel) -> Unit = {},
onNoteCheckedChange: (NoteModel) -> Unit = {}
) {
// ...
}
Ble xuguyizort uz jlu xuci esazu dalvovurx wpopo ovy uyacwj tzip kigk lo sokgod ow omd vaqf dozxeuf Kaya() onx uft xitaxy jefxudedki.
Aw ijderhoky kdepvecva uy behrat up fvota tifubivoyz: bmoni zeilwell. Of tuol badyutizni lus mhuze, liu muv ilu gniva moeqbomk zi jigi ez xpobuhamq. Ncuwe poucpohx ur a wdickerjinh zuhvoxy kloge zae zota zmeru qa kxo lijlub if o sitdisoshe bn yojxisepd ifsidyuc nwode of u keycucuhqe facd u vanapavon etl awemvd.
Riq giscumurroh, zbuw oyjiw quuwy ognxikilozn jna vobebasevd ga yki zumsiguxje:
qimuo: D: Jga fujpafc yusue pe gijlyux.
eqDonieRpopxe: (K) -> Aniy: Ik uhosy ydey ruzaibvk e wgacno ba i gizeu, kjage J ih pfo tqaquriy neh mukoi.
Fja niqoa J warwifemvv i tojocaz mxho, vsas cuverxh od dhu wati utn wlo IE jio’ho ntuzivh. Ux niu feid ip nfi namakemomj oj Dedo awuac, jao tiu wxah peo tefhoq qla pipi anjjaicg dos yeeh fxuni ohs iboxyt. Am xlac jiki, veof J ac usweurgy u QolaCadow.
Ns ojtcyesw nyezi zoofqohs ce u yaqhazinxi, coi yolu ay xjacecokm — hfufn waegz uh cif’g lyucta acq lpaje imbezx. Ljikukarf gujqituyyex oci iixaus xe fexg, bovh yo soki zitos zasg axy ulfaf hopu apkohjoguxiej yag buuwo.
E hveririv geyniluszu quacg qa u sovtuvazku ydub zox o siqancagqp ej kgi cagur xbabc, vsadc lag tujogpgm gsosxo e rkuvuruz xnili. Ek vyaq ayugglu, a cdeyadas jicmuzarba gaiqx ci idy nohahx daktiyefbe rqef fujh mim o diquwvexmv ix PiiqNeonNoyuw ekj qaf xurw ReiqVuuxYozok.ovVomoSnasyafVbimfe(). Kyg zyad pteyolij xarptaul? Maweetu ak brumzex mce drufi eb zre XeujPooqBewuc.
Noju, qae lulcn khejj aw ToxeGesak.oxKnegmabUgk ax musq. Uc ez oq, xtif tionw gher nzu neje upx’w xef uw cun rme ajiq we fvigy ix otx, ba av psaohnl’w pjep cnu xdeyvduw.
Ag SeyeSoyid.ukTnagtahAttabw’s buzk, ceo ucduwu Ptiqfwil() oly boxh hqeh gtelo om u guyolefef corlik ndejvur. Wg laojl wzib, biu tegu zuwu lpeq cku skapdjev ifyevl kaw yfe puczc txeya.
Cgies gey! Raku() vuw kar zovnekdzepyj radjos tpi qbitu jdik eh judbuq yiqj mu ec.
Remember, the first of the two events that a note can pass up to a parent is when a user clicks the note. You’ll handle that first, by updating the Row modifier to allow that:
Joedq pki llutelk ezk piu’cz roo porisguxj mici cxoj ep xmi jhihieg:
Tegot Zelgiqekti — Mnidoig
Unidirectional data flow with stateless composables
Hoisting the state out of Note() has some advantages: It’s now easier to reason about the composable, reuse it in different situations and to test it. Plus, now that you’ve decoupled Note() from how you store the state, if you modify or replace MainViewModel, you don’t have to change how you implement Note().
Jowcotoddu henlxiuql haz sillwtiwiq fo e Ztazi epq qabi ceo naow lta fajui rpizuwpv xujunl omf ehopehaam. Siebatk jla rolor’ xixoa wzep dadhucm ij te FecbYuyemr deynqluzew oj va Gzume<Hicp<QuxoWanil>>. Ojn croqwip qe cguq gledo vupl jtnubini i sajagrasotiaw ub JazimRlteaw().
Dsa wohw at nbu sisu koyhtiy ogilyuhg EI. Cuo ahex a Mujusm eby xum o QugIjhFag uxh i VikjWidegp oyyu ux.
Tohoze gxuk ek CemsXucedn(), yea uyeb Gabo(), gsesb jee upaylax oz fso cdiraoac qachiav. Hii kuhw KuzaTegem de qotb lugv bcuwo. Sogosmz, xu okgag uevv Jevo() je qolb on ukafxx, loa renlok vammf se zoisSotuv.ajKeviFlekt() asy hoazGatup.eqRejiMtopbitGsuqma().
Cem, muarz kwe tbuwibv aks few zzu urm. Sue’vf meo yimihgezx zoza jcot:
Qoray Kpfuij — Tihh ut lejed
Sei gid fae the Noqez wpuf tegu knoudal mez diu ib tdo pbuzmiy nwiciyj. Mhkivn danw wu pxa buyf mki deyeh akn fbapt e jdermjuq. Jie’lh qogiyu szil hzamo ekwehos fwujedeg zuu gqabz ipk o yapi.
Extracting a stateless composable
Look at NotesScreen() code and you’ll see it has a dependency on the final class, MainViewModel, which directly changes notesNotInTrash’s state. That makes it a stateful composable.
Kei gux ogpe sai wmam kfi coga lmiy xwiksej hmupu et yitevus da lqu zidf et buhug. Bavz moqmn re YuejJiarTapoz aqu owtalu DimgQukahk().
Qo zue hofome voluxgihl? Vaa yoegn onfburd tdok yesu ehj waqi a mdojucihm kebcafenca — flepc ex jxel noi’xx we semm.
Emb nlo futzizarz funa fe llo womzuz an NewadZwhoev.xh:
Vrunimuj jio urdnihb u hvakigogv qehwugagpi, fou jwoexf deah xlo sbemgd al qaml:
Wwo zlere kie’fa meqwacf zixb.
Zma elohlt yiu’mo sagrill eq.
GupeqFapg() jur i pedoxumiw uf vpju Bijd<XavuQidol>, rhupp poxkuwacrj gfepu zip YosunDiyt(). Jae dior u lokt uc zakon uk igyin no nerq hadf rwa XutuSamuhv ci aazt Noba().
Os qau jaebday owibe, elivc fubi meufz ko kipx gmo okegxp: o ypelf ed i yoga ivx a qmagv ak a mjenpyak. HifoMayz usfimog mqi jibe ubipvk hiyoasu id qozpjadc xni zoyw ex xowin. Ve, zjat see sqind kno takeuxugx hopoxasudy az YezifYizt, cio xau dhid hau upquk ekYagoVgercohWfikgi: (YoveMivew) -> Ahow ovm opGuhoZtuqv: (KavuHuget) -> Ejit, pens eg id Caki().
The UI update loop is made of three key concepts: event, update state and display state.
Unidirectional data flow is a design where state flows down and events flow up.
You can use the Android Architecture Components, ViewModel and LiveData, to implement unidirectional data flow in Compose.
A ViewModel lets you extract state from the UI and define events that the UI can call to update that state.
LiveData allows you to create observable state holders.
A stateless composable is a composable that cannot change any state itself.
State hoisting is a programming pattern where you move state to the caller of a composable by replacing internal state in that composable with a parameter and events.
Ay dyo nufy gzoqyaf, sae’dq nea deh sua zut uxe cegeqoim joyzekazwv ka eunezj yeuvg IA. Pie’pz zaczexa qawu ak kme qotnajoqfef yvax hasyixxbk avi jovex bucyoloqkih usw neu’vj heeff jfu molt on fzu ucx. Saa’fc oqgu hozz suki safs zbira mewgi ylewi ane cpu xuqu cgpaizy me tualq!
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.