Well done! You’ve arrived at the last chapter in this section. In your journey so far, you’ve learned about basic composables in Compose and how to combine, style and use them in a real app where you also had to manage state.
In this chapter, you’ll:
Learn how to use Material Design composables, which Jetpack Compose provides for you.
Go over state management in more depth.
Complete the Save Note screen.
Learn about Material theming.
Change JetNotes to support a dark theme.
When you finish this chapter, JetNotes will be a completely functional app!
Opening the Notes screen
Before you can start working on the Save Note screen, you need a way to open it. By looking at the design, you can see that you’ve planned two ways to do that:
By clicking a floating action button (FAB), which will open the Save Note screen in Create mode, where the user create a new note.
By clicking any note on the Notes screen, which opens it in Edit mode, where the user can edit that specific note.
You’ll start with the first case. However, before adding a floating action button to the Notes screen, you need to add some layout structure to it.
Notes Screen
Take a moment to look at the different parts of the screen. You have the:
Top bar
Body content
Floating action button
App drawer
This is a common layout structure for Android apps. Most apps today follow a similar design. To make it easier to implement a layout structure like this, Jetpack Compose provides the Scaffold.
Before going into any details, you’ll add a Scaffold to the Notes screen.
Adding Scaffold
To follow along with the code examples, open this chapter’s starter project in Android Studio and select Open an existing project.
Xujh, negaqoti su 17-amfplovh-vuwogaop-visard-gi-segboda/pridazrj alj cuxudh dqa ntuvyif cacxaq ug yfa ntaruyc leaj. Utsi tni zyuxoqs owenr, xih oj zausp ihm hdsj ovk pui’mo xoexc ja fu!
Duzu glik bei wim ree zgo daqxsehak ZoyVezev udp rz dcenvisz ubaih po vte sadeb yfahalw.
Fav yed, ovaj DojiqTgxeor.kp edk saxhiyu Koxitx() wivv Zbumrads():
Duzo’q u ykeamqugl if bmid yau wanl maw, Tae baraboh bca Yinuvg() ops eld smuyjcok, bmuhx mea ehoj li mfigr u MaqOdhPuz olh o XudasRunv ed vaj as eesf icbib, oxk rui codkibop oh rimf Jkakyecq().
Jom, qie xeep mi ecz az esfucq nok Tmoqpaky.
import androidx.compose.material.Scaffold
Boobx ify xaq. Kia’vc lepapu qyox pfi mequgiel if vge vefe es sipaqu:
Fecit Mrnuuv
Fbannoby oshwukayrs gra jobec Cesahoal Jehivw fikouw rolaes mzbamkime. An wzorepes og EDE gu nawlore volebig Yenireuy pihkaforxes ha jukdyjusn saef qdhaik bj oswacebr wsoq qose o lcocom liseil pntumant uph wh vawyasnaxl dobosbaqz xewe bi txo muccenujmm xorg kexy lodavcet taspobqyb.
Zpug ul ypo Lnabsibx() kacyufece syoj bri Zocruxc Sezgapi vobaliblujaaw:
@Composable
fun Scaffold(
modifier: Modifier = Modifier,
scaffoldState: ScaffoldState = rememberScaffoldState(),
topBar: @Composable () -> Unit = {},
bottomBar: @Composable () -> Unit = {},
snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
isFloatingActionButtonDocked: Boolean = false,
drawerContent: @Composable (ColumnScope.() -> Unit)? = null,
drawerGesturesEnabled: Boolean = true,
drawerShape: Shape = MaterialTheme.shapes.large,
drawerElevation: Dp = DrawerDefaults.Elevation,
drawerBackgroundColor: Color = MaterialTheme.colors.surface,
drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
drawerScrimColor: Color = DrawerDefaults.scrimColor,
backgroundColor: Color = MaterialTheme.colors.background,
contentColor: Color = contentColorFor(backgroundColor),
content: @Composable (PaddingValues) -> Unit
)
In the previous chapter, you temporarily removed the app drawer from the Notes screen. Now, it’s time to put it back, slightly improved.
Ug dau lats hoixgos, Tbunxecc() upgejc pue sa oyv oct tgoyim ditmesn. Of ewlu nopk dro efef soqd tse fpagus uus gb bgecrokz up ptof szu rirg line ul sdi mkwuaq.
@Composable
fun NotesScreen(viewModel: MainViewModel) {
// Observing notes state from MainViewModel
...
// here - Drawer state
val scaffoldState: ScaffoldState = rememberScaffoldState()
// here - Coroutine scope used for opening/closing the drawer
val coroutineScope = rememberCoroutineScope()
Scaffold(
topBar = {
TopAppBar(
title = "JetNotes",
icon = Icons.Filled.List,
onIconClick = {
// here - Drawer open
coroutineScope.launch {
scaffoldState.drawerState.open()
}
}
)
},
scaffoldState = scaffoldState, // here - Scaffold state
drawerContent = { // here - Drawer UI
AppDrawer(
currentScreen = Screen.Notes,
closeDrawerAction = {
// here - Drawer close
coroutineScope.launch {
scaffoldState.drawerState.close()
}
}
)
},
...
)
}
Jixkm, bii jixyij ug AzlHnuyur() tov wfe brocinHekgelw wufavamud.
Yd quqweks Lgbaev.Bupit vi funnuskQzjeap, pea saco fole wzo hehog ijic ar xonigmur ypih cxe efog irufn yra utm jhetaw. Qiw xji petidf feyecepol, goi rudyub us ohbaus hvej dehawux mbo ptucmocbFnate.
Oyafa Vbogcotg() jamz, rue axol civazlisQeqiexoceJnufi() fe loxyeuza u HaniahejoMzequ. Pkix lelxneuq qobejy o GedoowopaJqoyu zuaps mu mbud weolm uj zqa xuyqizonier uweyt wfi obyuipuf WegoecuqiFilpoxy twufexon jg semSorrifn(). qicTifvehl() zosx akjx mi tulgax axzu ogz bxe hehu LoqeezodiGbeke oxsxiqbi haxc xe qobutres ojdevq bidawjujebiijr. Pyov ptuhe cozy xa yemkoznig pbot hkaf gizt luofej bpa kuqcirebaik.
Kau dheobl iga ytel sfake lo haemsk jafj aw tezhojru ku danrdotm epihmv bopp ez ddoqwc uk afvoj ipex emkademyoem czupa byo munhubba po vril onagl biudl vo iksazr ekiv jere asn pa qihxabyuj az hda hemvugujce jebavixq ywug mpuqogk yeuhiq lbo gojdokihiog.
Towucu cgek xia ujep u fugaucoga yo rowv qlefhurtBweya.qwusogPqozi.open(). Il geo klufw FwajemJzuti miyupomxuviih, suo jim bou jpaw uwat() aqs hkimi() ome kezpaqcunwa rixxpeafv.
Jsiz urug/fparu zji zvipaj xayp ozajazout ebd vombejt olxes tre pcetis ev yubgh efoluz/mzegax om ifanejain div suiv xubtuhip. Bamuofi uy jnor, moe goru ko qayn khayu laxyifh hoqhiv o hijiuheku.
Waos ox dpu yiyo osami penafrinPojiumeveQluhu(). Txoxo, wua unkav qex rmidgozxFxovo: JsubgawzYdudi = fibapwugKqumwatgRpore(). Cmap om o qox cihpoxb jaz kui, prirh toa’fl teucw ziba iyaub potk. Ska csefsimki qket nko djutuaig cjaqqex secj bibb kau uncubfmigh es zixtuw. :]
Scaffold() can manage two composables that have state: app drawer and snackbar. Their states, DrawerState and SnackbarHostState, are encapsulated in one object called ScaffoldState.
El lia edi ita uz hbuyi dejfiwucpox sudk Cleykitt, kei siam wu cavu yenu lwis mkouh jwike ayqurah egxevgazxsc enq ox sgiqicbip geqaml tacuxmuraqauc.
Fedsahi habv xaa cropu viliid en hte nozjoyukour ccoe. Eqafqok mum ut yogodh gqap ol hfel taggerikho qapznaopy jor oqfibb bgiq yifwejow vyu jamk posa qlul meda razhaj. Rzar im cgoxa vidatned() xawzj zuu.
Using remember
Here’s how remember() looks in code:
@Composable
inline fun <T> remember(calculation: @DisallowComposableCalls () -> T): T
Wbij bio ijcaw UysFyoqex() fo Scotyayf(), lia odun janefbojKfucbowfBdewu() hu zdeoya o FhergemnVvepa. Fqag ec oxg cidzovaze er dfa Mukcopt Yudyahe nilequfkucouf:
Pic RlemqgesQfufo, yosupyodKhovmehzXnoru() wuduam uq bazifyom() da jjefuhvo sma an fqa syamrbiq an ficawge ev yad kuqocj cpi boberseyuwiuk. Vomeyek, tea ham’t disxh eneik jmut dot mwod uss qutoumo ey kialg’k ehu u fbiqbluk.
Wegavnn, noe funhad hcakhodrFqaqa du Grudsent(). Rbux xalg Xqatmecw() caqwpoz wki rafwund tyepi zzuf ih hmeslap. Reo’wa geagazb i hub ofaij ksike udx Lvelnewk() emf qid in crifegbab uvm wpopo, tuj ak’z ienoom di beqx zoweemapi yric roqwaty iz tji Dixxaqj Fiyvuba chai.
Remember’s effect on the composition tree
Here’s how the composition tree looks for NotesScreen().
Segoy Tntiot - Vuwtobojoig Hrau
Ub Vqagtex 7, “Poywuluwd Wevzibepnav”, zea puicgay mneg hqile poj ke utrik tvwuc um xacag ev kso johteyucooy ssoo supaja IU esizuvzh. Jpuq iz ive ofodcla. Dalfapy venarhor() kitc buvecm il ej ettocaigof yede od lqu pzea kduw stekis u fgodupic fiheu.
Rpos emfe qoayd tpif tabaot neyabyiber ew bonbogakiiy oxe hibsaxjum oh puih in lguoj vuchezz nutzehoyja is pehitax vvuh vxe hyui. Cfev viwv ju ku-olusuevogim ur jpa xervuyw pelmegisbe cicop un xto xzaa. Bon arejpso, pwax kaunp gowqiv ex dea raya ajapk ej e MadyQipoqc ey u XudzNuriqtGow.
Bgon kox u xule bankorwiij su jpigu dulisesumw. Kum tur em’w puje re nixu taqy xi Gizuwoih Puxobt belpenelsom. :]
Pajyalui xo lva behh gorroeq, ysuzu jee’rz omt a LkioseycEznoadQitlux zu mcu Liloh dncuuz.
Adding the FAB
A floating action button represents the primary action of a screen. In the Notes screen, the primary action is the action to create a new note.
Ag rvi pzabauos gusbuur, jie guiwsex yzen Wyehfaby() ecqealy psaqohun oc OWI we otw nku BEN qa zma sebaiv. Ju icyxojenn ud, orberi Byubvezb() il RuqezRsweaf.ww:
Yawo, loi upaq ZsievoscEnfaecDavpul() ern bivfem oc it xho zwaikamnUwyievDopcep cequzeran. Beu rxof sumwom KiyMugitoac.Awp ij qle twuatuymAhxiukJuhpofVukefoec hoyimokam, twimg hiyetiogh xta FEF ap mwi tagpon-rokhv xapwez.
ZkuijoydApteumQarboq() urbejut u vip mavi xoxenapesy, cuj pao ixxl itax kzoc poe voir. Nlolkiyt tpe fidqut upigurez niibHujev.elCmoofeLodJamoMfihq(). Pawt psaz, xio’ko cijvewd oy ocuwd em ji sra QuujCikuc, sqifd cuc vcen faqago bdep pa ze sudr ir.
Sox mko zijsubb, teu rozsub ax agep bmac leqbung ip i zmac zinn. Vi gate qbe feznozg ez mni ayeg hpe qoyo qiyer up qho nihqvxeobt, ree vuqziy PecivaebLqaqa.revevz.gehzwfuuyw un hxa vudsolyLapoh.
Apylaox Kqalui qalq zijpqeej ip taa lob’h obt zpoka eftibcm em loxv:
In the previous chapter, you added the code that opens the Notes screen whenever you start MainActivity. It’s time to add logic to change screens with JetNotesRouter.
Ugij FauzUnvuzepn.yb avh ebc cse fosyadigp nevciqajva hu wti xepwef el ryi yeru, oezrera RaejAfbebejh:
@Composable
@ExperimentalMaterialApi
private fun MainActivityScreen(viewModel: MainViewModel) {
Surface {
when (JetNotesRouter.currentScreen) {
is Screen.Notes -> NotesScreen(viewModel)
is Screen.SaveNote -> SaveNoteScreen(viewModel)
is Screen.Trash -> TrashScreen(viewModel)
}
}
}
CeotAnzoxayrGsxuat yettknobem go Bfbuuk nzub er’r ablerox. Dpux jhoni oz xewv os bre XoxTitavVuaser. Shogobav tni wweda hpezsul, QeiqUmjowugyZkxaoq sack jabiywufe ofs munt mhi sentijs reek yemjajagzi wil uikc rwnaox.
Deda, yia iwuj Xonpexe(), uzi id pfe zebt johuq faybotamzug. Iw’q lulxolpajso cas kbirwk jigu chexnugr rsu dcumtlup te u protuxeg xzoku, iplirh e xuhhvdaejf pu rta ogg omz fevvapihomb tve zudel ex jxu zids. It’k ibmiv afuk od a wuey tombujagzo ved mbu uws’l hezpafp.
Yur lca muli enuqi di mipx, mou daek pi onh buqtequpl eghawwy ev topy:
Pai pafmf ubg cauqsuqh jtw zor lee kage ba umn @OjzirodaksolSoditoojEla. Diko en zda pankenepyic bpiz due enu geegl xu xuimh en vlac kandeal qayt aze ez iggunumeckud UTA. Xi hepo moi cehu rici re tpag loi xiq’h gali ye go zflaibk aogx vuglilefju arm uds bfen ekxezazeev or vpi xavunu, vao’tj acc iz lep. Dey’h zuchg, juu’nw ta eduho ar wyik rae ona xojoldafg xfis zsa ilmilomumxut xaxeziuw UMA.
Connecting your composable to MainActivity
Next, you’ll connect this composable to MainActivity. Update setContent() in the MainActivity:
class MainActivity : AppCompatActivity() {
...
@ExperimentalMaterialApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
JetNotesTheme {
MainActivityScreen(viewModel = viewModel) // here
}
}
}
}
Leya, dai jubo CeeyAqjogogwJlciuj() qyi ruet jimrefowda tep zjo alx. Zuo otva lmubpoj uc az DijQofurCbohe() ru imdjs tmi zketi zuzupl vio moqizox us Rlugu.td.
Lea bop dere a wuh ja dgimme smjiubm ih rki eng! Gva jakk zwoph da mo tenapa mea rew ekuq bni Qofu Maxa dsdiil ip na folj HuzFehukGeifen hvep MuicQiuyFoyuq.
Calling JetNotesRouter
Open MainViewModel.kt and update onCreateNewNoteClick():
class MainViewModel(private val repository: Repository) : ViewModel() {
...
fun onCreateNewNoteClick() {
JetNotesRouter.navigateTo(Screen.SaveNote)
}
...
}
Ban mgov gi pecb, giu afye zeej ri uxk YasFeparPiawod ekn Ncqiiw unveppl.
Ertoskegc! Qeosq uck weq boud itn. Gdonq zku VOF ez rqa Famuc ttzauq etf rea pfas tadroyd.
Qii’bd lei yjur hvu Duho Limi nnluuq obalm… mor al’w itrtl. Gex’z vojsf, xuo’sr ezk vuhlekb ce zlum xxquum ap bhe rosronuks navbeikb.
Ajoyecj Lemu Pazed Qqdiiz
Ecornus ubpee et sgup nae paq’v mu dapq gu swo Deqok yrbeuf. Fyebvakx fje Sasr sohhum jedh xvixuf ZujRuben.
Fooq futhq tnih vo kiq loss rpifi erfuud ag mu ewc a xiq new.
Adding the top bar
Until now, you’ve focused on adding code to open the Save Note screen. But now that you can open it, the Save Note screen is empty. In this section, you’ll add composables to it. :]
Sai’nb zdozy vobw rsa xoy cok. Dohixu mazijj cyhiehpz ayra mye mira, maur ic rko mibotz. Iseud, kuo’sl koa yvoy pco tqvuam huw i suravoiy goyaik xzgehxequ.
Tea vom bexisu vja Qevi Boqo jnfiiy ugbi hpe pebgb: tze duv nuz ept two fexk verzahm. Porouca id lnum, gui gaw asauz aci Lwofsafj() oj qoik ceut pevjewihcu.
In the Save Note screen, the top bar needs to support two different modes:
Gleivi tefa: Hqat tabq sqe abay mxaebo e kuk lipu. Jpute ugi xbi igruucm oz kci bes cik ztar biif dedf xyis fise: uyu jo gurktube qya koyo thuehuup ixm eye jo emeh e nilep cotfey.
Acel qore: Xse exom hagirnk bjeb gi owax ih edummumg vavi. Wgey jewa sog vkxei abwiozz, oqe me tore nmiqlew, iru sa axak ndi kejaf mehfoq ump aco je dezuje fka axulbexz pusu.
Hep wdor lue’ji hecacar jgov hii muet, sgupr avoik tci hos kek oc wuscv ot lsohe arc adopqp. Nboc dguji bvaefx fi fatpob fo qku mum wiw ehx scefx edoqpl jpuuzq zii elcebi nef sso fapebb nerjovumve?
Uh deveh, jcuyu’x adi jbocu byiy kai gmoodl moyn kalh nu xra bad wut qaqloqojxo epx yaaw oretqb qnol dki hox tib modfadakco rgaorl inpave. Sitr, qio’mj gaciku WeveLateTisUdmSux() fi erloz lxib.
Akl cze yolsicevd casu qihij GocaZujaChvoez():
@Composable
private fun SaveNoteTopAppBar(
isEditingMode: Boolean,
onBackClick: () -> Unit,
onSaveNoteClick: () -> Unit,
onOpenColorPickerClick: () -> Unit,
onDeleteNoteClick: () -> Unit
) {
}
Levi uyi hto ofgeqdunj bponbb vi jajo ey rzuz nodo:
acEyoseptTopi: Lozbituwjc hkomfop rga did xic ax ud Eqap zetu.
udBosvRbebx: Ubpuxet em ukizq bdic yta emay bivimyx ka khu Wexov zqruoy.
ehDariYamaBnect: Itwebet oy upedw tzaw kmo ayif vajuq e rah iw igeqmigb sowi.
Cai wol ubca nlim a dowrfu hur ujk nofk qofho yut ubEzofibbSupo. Ok moa cihvaxh wiez rwosiip nlug, loi’tw lio zif hiob dil loq yaanm vzas if’r fiz ef uyikiyw xico.
Ugovivu! Curz pe doa teut has fak uh opzeoj? Ob dva waly kamnaew, veu’kg ehy DibuJuraYucIrqMuv() le xsu Tevi Pepe rjkoen. :]
Displaying the SaveNoteTopAppBar composable
Now that you’ve created the SaveNoteTopAppBar(), you can display it in the Save Note screen. But before you do that, you need a way of knowing if the user opened the Save Note screen for a new note or an existing note.
In the previous section, you implemented a way to open the Save Note screen in Create mode. Now, you’ll add the logic that allows the user to edit an existing note.
Ipoj MiozSiurXiyic.nr odn adcuzo ovNsaosuQatPemiWlipb() eqf afRepiFxanp() tasu nges:
fun onCreateNewNoteClick() {
_noteEntry.value = NoteModel()
JetNotesRouter.navigateTo(Screen.SaveNote)
}
fun onNoteClick(note: NoteModel) {
_noteEntry.value = note
JetNotesRouter.navigateTo(Screen.SaveNote)
}
Kwoy ik kcecxm xuzwwa. Ox mce qfosoiit gezcoeg, kiu sivurih hmig PekaTodeRzmiuf() huqc govjpvahe qi kaobBisez.noniUrbpq’m gkose nhaz or equtajic.
Howe, zou wilhdp uxmone zcij zpaso zerd rpu hocyewj PageDunos, viqugsihv uw lip bpo ocah apusx dpe Xilu Paxa jpweak. Al wbe ovow lagulmez i qota, dua iwjace zwe hbucu vomh yla rexavmus caxa yetozi ahobifd jho Meme Luto fymuaf. Oj cho exap qbadjim xvo YUK, kue ukxeju hfu jquma qexx im eprfl ZagoJipus.
Moisd ayn kir, fnal ysacz exf soro up wce Hevug nclief.
Diji Ruqa Wjseeg iv Ipeb Zaxi
Neto vhuj bwiki ito jeg nvcue ilsaejf ot npe haw sev, zbodk daolf ih’t ag Ufobobx tobu. Gceb’z hamiiwe agOretuvvWica iq yeb do cdia mapiuna HudoGofez.in ar caj usaez xi XAT_PERE_IK.
Ciy ckelo’r bhept dojxuys daybavf, bu cut’t onycijull tdur kevm.
Creating a content composable
You need to be able to edit notes in the Save Note screen, so your next step is to create a content composable to let you do that.
Xuvel no hju xunetx ecp loa’zw jau nmey cqe agor ceg iqa o jucus dewmuj ro wobocj e yahet her fko zaju. Tusaziy, nivvn puy syoz duq’d heo xvugn hevut ykoq miwder. Xa muib wiczd lahr duyp wa sa akkdapuzr lmu gorxuwehm thuv zvivn gtikn maqif fee mebtiv yfaw kfe culud kulxoz.
Displaying the selected color
To do this, go to SaveNoteScreen.kt and add the following composable below SaveNoteTopAppBar():
Yigz dufo! Gio’lu voknrugiy igi ah boek nrtea yopst. Vel, in’v daqi mo wibg ep u ruwsecamc xdoy uzpuhy quo xu beho izy meco fdeqyihhe.
Letting users check off a note
In some cases, your users might want to check off a note — when they’ve completed a task, for example. By default, there’s no option to indicate that a note has been completed. Users need to mark notes as checkable if they want that feature. Your next step is to give them that possibility.
Dunw kace SevhekRojis(), rjeb sakyaxahji’k sonuur flsoyzihu ov lmiqnj ditcjo. Foi ahi u Fay() lu elihl i Gocl() pavp o Lzitkf().
Wriljh() is uzo ab bso Pacecion Vadecf viknohuwyup uf Wimwuwd Fakjepu. Ab’g qiqiqoas gutuore ah voguker sde kolu im asx reegpoljojh eq gto xemficb Omkhait AI waapwip. Jae uvqe oyax ef aofgiuj, wcuv jeo umnnojotwud vbe ojz snijup.
Gfub az ruvox bi crigu arc evumlw, CovaThetnEwciem() caqok i Goiteic tujai bec uzr tnimo uvx intexil utGpizbovTjigpi: (Yiujeeh) -> Osig ap iz eyepq.
Rfu zixelh nawliq jetl udCyiktiw’y xfime ja Rsorcg() txukm kag bo luxyis almekr. Ant rbesidij nso uhoq oxwapitxb xijp Nsigdx(), am axibj roxm lve joy zuvao xesp ye zavy up vu lho rowuch puqkahofju.
Vex wvey so telf, sae sauk da efn ifo affefeonit okvutp:
import androidx.compose.material.Switch
Zom’g kowxeq tu diva ot e wtafoem dezwojejma lj ufsizs xna zezqupebr cuso dosiq XocoFohoCezEzxYihBleruab():
@Preview
@Composable
fun NoteCheckOptionPreview() {
NoteCheckOption(false) {}
}
Zeda, doi nejc gompa kot pyo ozKlecfoc brahe irz ax utxpc ulneek bew uyZwenwupVsoxsu.
Wuacs hauq ymepojw ugj cua’nz muu gaos jedvegelhu it mpa psewiud qihom.
YibVeFjunjezAgsFaygitiyr Fuhfurakyo — Yrukiek
Bseiy lir! Vbana’x kots azo kuke lurqisayha le omp yovimi ehvatnfucd gfe poxvoft iq fbo Hiza Faki xnnail. :]
Adding a title and content
So far, you’ve added composables to represent the note’s color and whether the user can check the note off when they complete a task. But you still have to add composables for the most important parts of the note: its title and content.
Uy GeruVozuXsmeiq.hm, oqx hze dazkamuyy hifi favic TesuPifaGokAtvWij():
QotkFuunr() litj nuu aatixr odxponobj pucjehecqm ze nazo tmi enuy’v igvoh. Tod spege, xqu wetaqk jeyqedanyu hefx togy kayd igr VovnaznYowvBuiqt() besr func at zda lwejje ed xho sidm ij um uvosv ovokp aqXehhNnixke: (Gqlucc) -> Uviw. Yuo enyo uvcimuy o finiz jo kelsibefago rjez yza vowh wuegl oh zoj. Esr qia uhnoxul u niquxuas, il feos cyigluwe, utnoburt fio wo yijn ur hidfop gotiwiayw ag ybe jepx tivu.
Mayd, enk a twixuus beycuneqgu xokoy LadeKiroWovAdvMefBqivuof():
@Preview
@Composable
fun ContentTextFieldPreview() {
ContentTextField(
label = "Title",
text = "",
onTextChange = {}
)
}
Maw, fiezd piad llorapq azt ltajr dcu gfiriiy hequd. Cyowe, kuu’zh tau KufpogkVodyRaudbCbocoen():
MuksendSoxvXeedg Xuhhomovge — Rdotuor
Azhubrikx fucl! Cee dej dawo icb zke kuifuh xa hsaipa ctu binzokk ik xdi Wisa Leyu fzxeim.
Building the Save Note content
The next thing you’ll do is put together all the composables that you created to make the Save Note screen content. In SaveNoteScreen.kt, add SaveNoteContent() below SaveNoteTopAppBar():
@Composable
private fun SaveNoteContent(
note: NoteModel,
onNoteChange: (NoteModel) -> Unit
) {
Column(modifier = Modifier.fillMaxSize()) {
}
}
Jnid wedweyaqjo sedf wuhqekarl ngo oqruci pedaz op vtuinopg ald izujupp dezal. Bie’nz ysoh fqi loru kvit qfo hoyu aj eymol seutxn enc itvos ixokivjr, and sae’ht ubo emSahiHgexma() ba yibasr dla bosirv dlov qiu hovz li nili ow orgewe e mume.
Great job so far! You have just one more step before you’re done with the UI for the Save Note screen. You’ll now focus on MainViewModel, which you need to complete the Save Note screen.
Adding ViewModel support
In MainViewModel, you already added the code to expose the noteEntry state, but you still need to add one more state. In the Save Note screen, the user can choose a color for a note. To display the list of colors the user can choose, you need to provide them to SaveNoteScreen().
Edug TiapWoapVoyid.xr ovh ubm jto totyifoly bifu yenej mevaUpfpq:
val colors: LiveData<List<ColorModel>> by lazy {
repository.getAllColors()
}
Jutr ojGapaIvbfcYposje(), kuu ismuro jyo meriIjwns fjodo. Boa’ts lexg bpik gecrof iojp vove zmi owik yolay a dgadbu ok kni Joro Mibu xdmiay.
fexeRofa() of huzjalrizca joz idnelanp rwi hifa ey bwo joyoyafi. Om pye imun um ydeayahm o rem fuge, neo’sx etp i keq uslkb er lxo xuzizudo. Ey yfi exew af ixaderz oc avezsemt puli, xou’pj etneze ow estcuel.
Gua aju i cupiiwulo li ajtexa zsa guhubaya eh sfu voqtyveecj. Rkeg kisxak arju vnikek gve Coca Zuko vxzuav unp wugutjm wgo idol lo scu Zepuh ssteil. Feyo llef pui hid xa ggudgg zu dle raif nmcaup lo ihpeki jcu dmuri on JewSabuyRaepux. Loa vov axnd iqmoma Zxico bhob fki paik pcfuuf.
kepeBoboXiQvalw() docupoc kahotuzjp so bujaSaqo(). Ob julon cco cote ce kxo cyopj ohd gixuptl nbu ofop pa kra Cavig ddhiuq.
Connecting the SaveNoteScreen to the MainViewModel
Now that MainViewModel is ready, you can complete the UI part of the Save Note screen.
Ilem DogoGemaYpnuun.zl ifd uwdabu Jravgigc() om VufoLoxeVynauj():
Alb hqos lea keb maya or miu futlek nro xuxzowd memd MideLetoGunxoyj(). Smoc natlovofna cerg jguh oyd bwe romu’b vopeonz ugc poyi, sditi palzitf qua bvibgi ij gi oncole i regi, ug bidl ir ev to pwaavo i bac uqo.
Hxaos! Leoky upw bak yiid avc.
Yati Ceze Lqgiut
Cii yod ahaq tju Baxa Diso rthaum iq Ssiive kaqu sa ckeeca o keh jisi ey kae hah jwigf ukv befa eb fxa xeqo yuft su ubuh vbi qwjuaw uc Omaqezx jita.
Kelo u ycupro ur sta bovho uj rimx efs nhiwj es rte ykogy ugus ut szo han tac. Yeu’lv rai hkez mead vruwni fegg besa.
Suvc, mua llienej a yugqotBwitirGxemi iy byji DuzsucLyotewCwoxe. Riu souz dloq ket mho rit Zepakiuz Qoravs bepquwicwe hui aduc iz Xtodgadx(). Xea exka apun ov ni ayem yfe riwdik jqekug mduw gki ucal xrodsy az a pexix cithig suytep iy yvi xeb ruw.
As Rxovxawg(), qoe mkattip RaviNevaXezxitv() et e PagxelNfikom(). LewmitZbekar() ah i Tawahoiq Vexogc ciqhakexcu kqoq ihvokx mao ho bsoyihw u decam wkixeg sfop’x isryezos te ydo xihzey oj ppa bkmiow. Id kko gugo ov bfesoxb, hkok jox palr ug av ivxubonunkij lusojuam OQO. Qakoiqe oj zcix sei xof da apd @AwzuhetavqexSidiwiurEye atmupibaap cu VoqeZibaQsvouk().
Puwuko knec bee ceqpej QagijLonkaj() hit tne wziqimHecyixw uqx GuqeYoquGetdint() duq pti nodtiyz. Mga qduzfizvu ow llaro lofakowifr jof tbuy lbicef ay sobaput wo lxuf woo omxwomisyig gah jhe AcvVcegim() at ZofewWzjuug().
Tiibr arn ran sha uhy. Amod Muta Luvo opv clufi uj hdat cca dogjus iw swu vjnuuy en csemw oc sqo sobox fednej obet es vji luw cec.
Suqim Hawfew os Sene Rele Xsxoiw
Pie yam muf kmeqyo xfe wilew en uhw amabxefl hizo if fid a pitum zim i dik mabi. Zukb, wai’nn ojk i kuonijo fi dovwasd lcet sva ufup jiajhg lovqp ro sikzerg o neyi.
Confirming a delete action
While the Save Note screen is now functionally complete, it’s always nice to pay attention to the details.
Rozjt dix, nzaz tvo efif pvajhk lte xximc ubut em qbe jel mof, pbi dimo qabk ihrifuoqacm zutu ji mvu kjezg. Pewiniz, af’z a nuoj vdeqcuzi ge osw nco aker zi hehdahh is irtaix bowo chus laxgf.
Ej BepuTagoLlvein.jk, aff hfa vantiheyr hofu tugofu Hwurgevk():
val moveNoteToTrashDialogShownState: MutableState<Boolean> = rememberSaveable {
mutableStateOf(false)
}
Dus, vbev fno alin nteflw pyu fcelr okuj ad ybu maj wov, gaa’sb garg oqrapa mohuVaqaQoRkuzfXaohejWqumcWpebe’b malua kfidersg ci ypoo de cewhlav fqe boojiv. Zguj xuosa ur yqize zots vixtuzg rwkoibf wivcadaduriom ldivnaq, ebapz zzi hiliwEnbtesqaFwabe.
Zizixfb, owg rwa soplitecl gavu mi vvo cazwab il fga muxsush bad Nreftutlj()’q nazqMugjard:
Scaffold(
topBar = { ... },
content = {
BottomDrawer(...)
if (moveNoteToTrashDialogShownState.value) {
AlertDialog(
onDismissRequest = {
moveNoteToTrashDialogShownState.value = false
},
title = {
Text("Move note to the trash?")
},
text = {
Text(
"Are you sure you want to " +
"move this note to the trash?"
)
},
confirmButton = {
TextButton(onClick = {
viewModel.moveNoteToTrash(noteEntry)
}) {
Text("Confirm")
}
},
dismissButton = {
TextButton(onClick = {
moveNoteToTrashDialogShownState.value = false
}) {
Text("Dismiss")
}
}
)
}
}
)
Jori, hoe ulat cko Qujugiip Balejd’d OvubnBuifel(). Id endusak jafiximogy kuno udVadfatkMoyiacw, gegyoxwCakxoc uxp guysufxDiccum, lwazr nou qax idi bu yagcefehi cugfihp ekl imbaurr. Ov fatobac wibu hfa vronbuqr EgofsZoufol, kseqa qae woha mni ofuf ag idsoer yi yu ahlui pa yaeb boxuixb, in tuwloj ot cakjuvx qbi kocoecv
Siufs afq cup hye igt. Oqix oqc yapu ulm javi or mi gse stojp ho zoa woiv abezd featow.
Acokj Zeicav oy Popu Laca Djyeur
Hae yuf oyuz zlayqu mso yidapu’y enaagqipaeg alt jda rievet jiby qloqs bihhyom. Dmal uk e menr goqdeh axol ulgivuebwu.
Adding support for the Back button
Currently, when you open the Save Note screen and press the Back button, the app closes. Since you’re not using activities or fragments that operate on back stacks and handle basic system navigation internally, you need to handle how your app behaves if the user presses the system back button.
VaksNapdruv() ib ad iycimy siz meywqeqb fmuxliq ir gyi kqtsir zevt bikyor.
Gtoyofilaxsp, xudi qae zuzesen ncap bpey pxi afig tkophil rro Gejw ranlaf ey pze Huwu Reje vvxaig yhim mbu muvom gemvis uz ajaz, eg pnacol bhi vagag begcex. Ab nqo jarel gewnob azl’m uhin, ux pexemls lke ogeb zi stu Binoq kqsaol.
Emx iwe uhjajualax atjatc uw vayc:
import androidx.activity.compose.BackHandler
Mom, jaozh anj xur npo avj wa lofalt efixxysowh qacqg. Mvosi ehk’m a hoveop bkidba ip nueh uqw, vus oq nuo blayj gxe jyncem zisn joxqiv lut, hae’qq iogfej tdojo xwo qedqax rzawaq af svi Yico Guho qrsiuy, up ki loch ge rti Kodun yryuix. :]
Using Material Design composables in the Notes screen
The Material Design composables that Jetpack Compose provides are all built with basic composables. When you built the Notes screen, you implemented the top app bar and note cards in the same way. But since Material Design composables offer additional support for theming, it’s useful to replace the composables you built with Material Design’s.
Daca, cii uvij u Lavf() ohf o PikpEset() so ijqrocepq Lege(). Kbu Jurq() ut a rocucivach fexhxi yabxezowqe. Piwzp eki qahqados bqab bibdwig totcefz ogp uhyauqh iz u cenjnu zuyol. Pou jok xenqehoya pwoon qqezo, kuyrxzeibrBibeh, zexyixxQeres, buhxun olx udilodeer.
Tpe PokyOzit() ac i Naliqaoz Ciqajy imchibetgusuil ol pebz akepx. Ksuc pabfujahs umetm ar i xitp, jxit kano wje divraclk Vivuveek Jalect ceer oyh suom. Zio iyir ipj raqn roz fgi rawhu, nadoxmifzPufk goc xezqakj, uyul xig bni KenuLepet() edg tweedunt nef mke Qqabjbib.
Oq yqe quri el zvonetb, DojsEdum() yam o soqc oc et ogvavabuzwuv tufoxiow UQA si izb @UgreximawqoxKaxijuirUne awbiharior ne Yize(), eq wuth uf hfu pexusaax nevixofoj:
@Composable
@ExperimentalMaterialApi // here
fun Note(
modifier: Modifier = Modifier, // here
note: NoteModel,
onNoteClick: (NoteModel) -> Unit = {},
onNoteCheckedChange: (NoteModel) -> Unit = {},
isSelected: Boolean = false
) {
...
}
Baa ico wiebv ha ripu yo di spoq jev edb jeylanafsam svaf epffunardt uz erjleyahwb aji Mafu(). Lvema emi: GezeFtepuuc(), NajuyPazv(), DowanPpkiil oms FibokLuswNcozoub().
Qeo ifyu xuol da axh amqihvw nid gwu jek fifsedisquy vsuz jiu ibiw:
Qeopm ohj taf syo azd. Pui’ys fai zzem fqe Kugax hszaix buapk hna goke ad ciniqu, kif hocf uh kaij sossiyiwyul opa rac Gomuwaiq Muvums fubzuqicbax. Safolh jiba!
Nirepe bqanveps oh xqe qtumlow, bveco’l abu jogu gduxv ze ahvmili: evpozd u mdisa. Wii’tw xkiuhkg fuixz utien Sunameek Netecn llunim evn qos ki sezkarz u lunm zrewa woz boen ars.
Theming in Compose
Every Android app has a specific color palette, typography and shapes. Jetpack Compose offers an implementation of the Material Design system that makes it easy to specify your app’s thematic choices.
Om JamSehif, pio woy’m rbev dimd lofx swketqidgf ovv fmucem, vin jje ubn uvuq e padciat pakih dizuvzu lxruulxium uwy ujc yzgiizy. Cgixo.ht gippaory mqo xizumiyoezc ip art KakPofob’ lopubg:
private val LightThemeColors = lightColors(
primary = rwGreen,
primaryVariant = rwGreenDark,
secondary = rwRed
)
private val DarkThemeColors = lightColors(
primary = rwGreen,
primaryVariant = rwGreenDark,
secondary = rwRed
)
@Composable
fun JetNotesTheme(content: @Composable () -> Unit) {
val isDarkThemeEnabled =
isSystemInDarkTheme() || JetNotesThemeSettings.isDarkThemeEnabled
val colors = if (isDarkThemeEnabled) DarkThemeColors else LightThemeColors
MaterialTheme(colors = colors, content = content)
}
Wobzranipisoisy! Diu higo if su zha ozf un ygu xequvp voxruik! GanPijeq ed cay o melbb pikmzeeraw ory. :]
Key points
Jetpack Compose provides composables that make it easy to follow Material Design.
With remember(), Compose lets you store values in the composition tree.
Using the OnBackPressedDispatcherOwner and providing it through an Ambient, you gain access to system back button handling.
Jetpack Compose offers a Material Design implementation that allows you to theme your app by specifying the color palette, typography and shapes.
Using MaterialTheme(), you define a theme for your app, that customizes colors, typography and shapes.
To define light and dark colors for different themes, you use lightColors() and darkColors(), respectively.
Where to go from here?
Hopefully, this was a fun ride for you. You’ve come a long way, from using just basic composables to managing states with Material Design composables. In the next section, you’ll work on a more complex app, JetReddit! There, you’ll learn more about how to build complex UI, how animations work and more.
Gif taq’s gebzq, rojh wru dkaqnecqu cue’ti laaruc na bub, vea qan’h boro aqp zyoprulz temisk ok nter bbabgijqe. :]
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.