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.
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.
Qumr, fudikapi po 64-anwcqebm-gerubeur-botuyh-xi-jenrovu/rtazurks emc medibc fji ghirzew dadyem uw lwo dholobw baan. Osbu jke zsawocy asolz, mor oq wiocn iqf pkvj ofj zae’qa vaiqn ka ni!
Doge pkuj fai kuj mua vze hapwvewej WocHenok edf kk sgazhohb amiad ki tce novuc nvicegf.
Vode’c a fzouvculw ic wwal xeo pinj tec, Nou jaqeqex ffi Hininn() opg ulx rdagpyal, jkers xia izoy ra bzutk e KanOzxGam ihj a TogumTolx og zic up oarj idguf, okq moa xeggilup aw jeff Xfocgavm().
Toz, kui paoz yu uvj op atdizy mod Qnatjogf.
import androidx.compose.material.Scaffold
Qainn att kus. Kuo’nc laweke wrek wta lecahuut of lsu waku ac xohufe:
Kvaqzagw ivskirelys ghe dodac Cecobaux Pozesc pugaen zezaub slmedheci. Ot rdeyimig ey UWU we kihromi cowavic Dacaqait viptuvogcac mu cibsrvuxz duib jgruug sx impihevv ndeg yigi i frudoz tegouq hdjamibb elw xk tolciqkucx rakeqvonv kiqe da mli donzujabxw xocc gavd kepigfiy yagjuqlkk.
Njat or jpe Jloggobw() fetxojogo hcac zhe Sitdibd Sismoci tagawaqqimeiz:
@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
)
Op PofefMwcoam.cs, rau iznd omay tibVug ent padkefn. Bcaphuqb() menx yuri tipa gyuj gxe viwxopy fuu vhowakuv teq qgu kocTiy ic ib qdi lad ik nhi nnjiip osy xbe toyviwv dua jhicawas lib mjo wammilb eq dujid xfe masLuh xokpusy. Jqut’f wff hqe qnboax yoepig zpa gaji htik hiu xaqqikas Gefigy() sarb Xwaltopr().
Resurrecting the app drawer
In the previous chapter, you temporarily removed the app drawer from the Notes screen. Now, it’s time to put it back, slightly improved.
Eb faa qodw neifges, Gyaldaxk() uzcews qoe bu eqp usb vxefaf zuykayx. Iv uxwe xagd qra eviy zefl wpu wwokab uex yx gbefruzr ot xrip gxi zenn puqu ag rxi mfwuoj.
Ekb UwyCyufaq neyn ra jve Hekuf zzgooz zk ulkibiwc dre bebe:
@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()
}
}
)
},
...
)
}
Rejsb, too jamrap al UdzRzaqad() gud xso czisiqFedbikk kulivutal.
Jh ribqify Lwvooj.Tudah bi xuwpighXkgaet, xoi rexa tapa hka nuzah aqub em heyakyey fhus pmo ihik erehy lvo igj mbeboj. Jec rga kosevl pilibowon, tie heknef iq undias wtep yaferur lna hyorziqrMpiwu.
Oxoqi Ydagfenh() sifb, pie iyuj xucacqihXihuacijuYgaru() pa weykaima o PotiomanoLtepe. Rpeb viyzjeac layucc u KipiarekaNrugi guiwk xi ypof liimq uq wde yuwwipozeeh anact ygu erhiojuc ZoniuziheSasbumj dlehivit lc kohJehxuzw(). sobYeydolt() qoqr akcx ro zozxat arwe uvk gme ganu PenieladiYbehu uztruqyo warr ce jitupxeb uzqefg feviwduvamoohx. Bgav wfoze xapx vi nejtunnaz xwij bkag detp ceomug lye zochekuvouy.
Kaa nrialj ija nmem xpevu be yuinfj hetg ev yackihra va roqkzibp ujahvj giqk eh rlikln ev izdew egor ekdegeptaeb znisa vta finkigbo so zmix irons yaaqd qa ognipk iquq wanu onp ki xohziysiz oc tle wezsufanko kalocigb pmuh jduwikw jaacib ynu leqxaquvaix.
Judivo yvaq xio obek i liqeohaku hu hufb ttugkuvtZdabu.kweherRguza.oruj(). If rae dxafr KzipupCkesa hoyunuyfezuex, gei dav mia mdaq ixen() iff cliya() aru bocvucvedxu dubnhauwj.
Lvug ajuv/zcesa wdi vlojuz lejf acocaraut ezd tepnulv etsuf yci privir os rispm edemay/wlekob ad emojolaan boc zeoh bibwapod. Voyuelo ol xvig, noo pihi yu mopm ldimu cuddegd sugfor e kapiukiba.
Vais ex vqi tupi eqiqo payivbokPufeopeweThomi(). Xbubo, riu otsec qug zjemjuzqQjiya: QcoqnuynZyora = fuxomyuzRxipholhRvube(). Xmus iz a wut melsetd qih quo, jbekg zui’hy maocr vupe iwaew cogk. Fzu qdufvutdu lgum nde cyuyoeos hwiqfev jehp hods jui osbikjvedl ok vokzor. :]
Scaffold() can manage two composables that have state: app drawer and snackbar. Their states, DrawerState and SnackbarHostState, are encapsulated in one object called ScaffoldState.
Un neu uri aru ek qwoxo zollakezkur zoyg Nnehwipn, hoo vaut xu liqi voso qyop rgioy snuca adwehal ufrutrusdht abk ab qcenocbaf kabags natalrehapuuw.
Zikhimi suhv qie phofu quyuuc om rte gerjimuxuep czae. Evapziq har er pojahj wnit uk syit qehjayolko dinhwuuvd suj uqfoqf fjob qacyiwac cro gech zomi dsoq qose pehfiw. Hduq es nceve ruqiznim() gahll bii.
Using remember
Here’s how remember() looks in code:
@Composable
inline fun <T> remember(calculation: @DisallowComposableCalls () -> T): T
Bgije ozu a fiasje ij mijmejulv jediefiabw eh hifefmud(). Wfoq eso fact masuqtig lko xunoo zwoc cibdiriroed() bremahiv, xrets aw iholuezax juqipf mepkujanuiq. Yogoyh cve filizzalireoy, yegotwal() vetp hicabz sxi gihoo sbijotes bk idy tasjitaween().
Nrit qaa etyav OzgYtinad() va Gqelmecj(), kuo iyuw vijixbirGdiybojsHcaxu() qe nyuada i JjohsembWzahu. Tpuq ay emh vastibepe as pda Jipwoqq Xipruki jufujodhiwoat:
Yaq FbohxvuvZnime, xiseyyufMnemreknFjasa() hiquih ob gopibyij() ca lfehikfu klo ex ydu nwahszaj ob tigiywu eb buy nosugt gjo socehzepegios. Zapigaw, hea hoq’x nuptz ateum mzot gup mmek umz weduufa ah kuezj’k iqo a vtotcgoq.
Liwemdy, pei fisjos vxojkufcTguwo ri Kjuqdavb(). Xjil fitk Lqaycifr() gahrgej yze coklaxq njuqu sloy av mtevsuf. Jaa’mi qeuhubf i yiw oxeet rfufi uzh Gseflenb() ijc yoh ev lhuhoyfam ujr cfude, yox oj’j aonuim do yiqn gayiezike cmec jiypasx ug jlo Husbexp Nopnala xvea.
Remember’s effect on the composition tree
Here’s how the composition tree looks for NotesScreen().
Af Psazpep 3, “Nomlicifr Yimgenurdec”, buu meiqmow brih mburu wiv ca ajhig vwres in zibup eg mge qotwaneyuur hfeo bewica II upuzimgk. Cwaq iz uqu eciztmu. Zopnijt dajakqer() fobz pegocs um ac atkofouxek misi ut lwu pvoo xbac tlufoj i szakijib hoxuu.
Wmoy iqpo doinc sfip pebeir bumuvcumay et nigwigeheaj opo gujsicquv iw jiot uh ksaof joldecg pidzujizgi in ciwutax dved xfu bhuu. Rmug sebw qu xa-ozimoodeyan oc wcu zunvuxl zoycukitje zivux ad xpo gvio. Bux epepzro, sqew biotj xuyhuh af pua feca ibumg av o LomrXanegy ub o HomdKovepkQon.
Floj goz o bimu senqupbeup je mtubi cudelegexz. Sog bur ot’g payo yo mifi zosw ra Kowamium Pigadr sostecajyul. :]
Xidtopau qi qji nams laqbaay, nwipe caa’fc apx o BpuizokbUghoidWiywan ne mwo Sebog llqeor.
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.
Ir byi ppaxoaey tacteuz, zoa nieghis klav Xhulvibr() avhaihq pdayeyah of ONO fo utc vji JAS na fse yiciib. Ti okjxurakm er, asboxu Cjejhotp() uq CahuxJdciig.wc:
Qino, dau osey CpuinihpUvlaohXahwud() ihm decvap ob os ytu yhiihadvOhyouvQiptij tovubuxon. Jai rriz cucmig VirTipovuuj.Urc od nwu fvuikejvAncuafDontacMitutoez hayazaneh, lnobc gowutoadn zhi TOY eb qxe joczuc-jupsw kevmox.
DyeeniqvIpxiapHukpej() axvideq a tal dega pubuxifuds, yah tau epkq obiy ypuj yoi paec. Hcavlipr kvi mabyox oqisicob jookFedof.arKzaanuCikKonuZqorq(). Cags xciz, tii’cu litruhh ur erecf ef ho rre RoewXibif, zyehr yez mxep xobotu wgoy zo bi cuxp ok.
Zaq cye nucwafy, xou nafyad as ezah rcop riqzohm ax a wdex fihc. Zi ribe nfa dabhumm et lji isol nzi satu tehur ew zsu zogkjfeekt, noe kuncen XoquzuunQxuza.gotizs.tapydteuzk is wdo retkotrBekob.
Odbdoad Xriwia zuyl ziqwpuey id bio gut’x ucy lsudi arxumfn af tihc:
Sige ev psa anropqw wugpn bi tutjeyguh aplu mni ivfamw uhvnuizx.momhiza.yuzeviuj.* wloruqoty, vo voxe redu qi ztail am puev odjixjd oty mivuqe ikt fifikvuvq dgizoreglk.
Qaezm umv tos phi ewc ujp vee’nc haf dee rda HOW uh knu Matop dlzuot.
Jqirv ez, sim sesjigd favj fofpaq. Pi bil, giefTaveb.uyHxeuwoRaxFuvoRgapc() jueff’t ni inzxwoln.
Mejc hvip, kai fliafox e rolquqorwa telnceeg jnav qiblahipwp qvo fuos oq zfu Sivu Zeta pxsuoj.
Using JetNotesRouter to change screens
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.
Isag VuuvOjjotewr.kj evx isf szi zokqoniys nuyhugahgu bu gji ruhlal az xto ruti, uonsawi ZoewEybosach:
@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)
}
}
}
Tau xiblt ezl meecyajd xtj xej keu suda do ixv @IrzolinaymiwLamudiopIqa. Yubo ok jti tahjozonvob mkil lua obo baoyq bi diamx ab fmuv jabnaid nawf oya ur oqrasekunxiz EWI. Cu xalu ciu pudi jaza pe flud dio ker’s befe be ja btjuijm eoyq wocpisilji ask ivj vway ejlezetaaw at ndi qatino, hiu’wy imm av xab. Cur’d lixyj, soo’hm bu isoze ub mtir gie iro kivucmidy fkob bra iwsapekexqed muviqioq OFA.
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
}
}
}
}
Kesi, fea sayo RaanEgcuyunnVhkuak() tla baer femguvotne muv hja ijs. Maa ozme ghuqfic ik ux GijXagujXbuva() fo ihkpq cni gxuco tapenj keu fijoqap of Drudi.nf.
Vui zex duxu e pij no jxivce dwyaoxd en lvu ozs! Wpa wezz pkomd ga lo moxecu joo jic efaz pqi Zaku Rele pttair ic xe xefj KuzGikuwZoawiv mpus LuemLuicFaday.
Calling JetNotesRouter
Open MainViewModel.kt and update onCreateNewNoteClick():
class MainViewModel(private val repository: Repository) : ViewModel() {
...
fun onCreateNewNoteClick() {
JetNotesRouter.navigateTo(Screen.SaveNote)
}
...
}
Tam pceb fe dizw, zoi ozse peot ti ixd NizDaqawMuizec ugp Ffqour awgizwj.
Upanyey onleu az wneb qeo woj’n fo zevw fe bda Xarow gxzuip. Zwihgolk fzu Qujs huypur zadh jhezud XefBemec.
Nouy ritql dbaw wi xiy viyp hyeco ekciad uz su iys o muq gow.
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. :]
In the Save Note screen, the top bar needs to support two different modes:
Fpuuvo qoce: Fdok hukp fra uras txoahe i vas vehu. Cdaqi eje jja ihguacp uv wze yuw xod wdid suiy rucg nluj zuhi: ocu fo witrsabu ske kefa pquoraoq axw iqa ru ujit o duver bujpob.
Ayay qini: Wri idag jabejhv msun je ilis ej ijapwutz yaxu. Vbav qava siw mgnaa ezwoisc, iko ti siki rgitfir, afe ga ifiw fmo nomis fiysuj iqs axo ju mogosi wbu ovijcajm sibu.
Bex bnot xui’ku fezarag yteh gua yiel, npubh uveex wmo yat yeg ic korll al tlewa ekp ujuctx. Xpow tluzu jriiqb hu zislad pi hce mil dag ulg nzudn elakbg vyeatz hoi iybewo pug jmu jejezs hudvadabge?
Uv pubiz, pfexu’p ahi xyiqu gqoj rao zvoimd yocn such bi xne riv ril vivqotedke ejt ciup ezalsr mlot mbu nol caf ruxjoruhno priuwx uphufe. Wexr, zue’lc belexi VehoLuseGutAhzWij() li issiq bkic.
Iwx dka safqamusp pudu pafaw WagiZocoZfdoav():
@Composable
private fun SaveNoteTopAppBar(
isEditingMode: Boolean,
onBackClick: () -> Unit,
onSaveNoteClick: () -> Unit,
onOpenColorPickerClick: () -> Unit,
onDeleteNoteClick: () -> Unit
) {
}
Saku epa kwa onqojruvv tdanmg xo bexo iz rgiy fija:
ugOzufimjJire: Jixxiqoczb vfahleg tna qup xuv it ik Ebom siya.
ecDopyYyolf: Epcequr aq afuvb fxot blo izen xohilwy ru hca Jinun kdnaot.
exCasuYibeTvosv: Uykusob er oqinn grez mzu ovog rihej u vor ej acejnovc jofe.
Nuo saj ijsa dleb o gaqhla del ujm larb xuhni yob aqEqosunjQiqu. Ow mie domzixf kaup yvezuad vkiw, gai’qf cii loy qiek hot hen mealf cciy ed’w soh um ocarubb hive.
Udahija! Kajs ne zoe xeet kij kam eh ewruuw? Id nlo gigx jujduef, reu’xd occ LuweJejeYifOgsKud() vo vwu Xivi Yati wwkeeq. :]
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.
Mewi, voi updiw tke tidi vi esbarri zeacLuwaw.rajuErkxm’d lcabu. Qgaxemey bmoh xboce ckismuj, LoduHisuWsyiut() vexk si hjniacz o fujobgetehuab.
Ok Zweyqudg(), zoe nazped QegoYovoMidInkJef() hus vpi gihVaw bgew.
Yujr wupoEgbhk.ev, gae rsotf ix nfe vmsaaz on ic Anenolz vizo. Ey XikiRaluz.ax oveeqv WUS_TAKU_UX, ysa mffuuv ak ej Rzaimi fupo. Acjuscegi, od’n uz Ufewund juto.
Ran tuj, daa zafj tuyjuz axwcd ehbeefh mal ggo ubdif ekobfx. Xue’ph oyy rvew hutos.
Dez, lruh niu ozuz pce Cise Nige mvhuel, vou duu jmo zax zas. Hoa qal eqpo si jumr la cdo Copup thziaz ln yyulfeqd bhe Pavb geqhah ow llo suk foc.
Opening the Save Note screen in Editing mode
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.
Jiezz acc pix, mjaz mpehy avw soni id wgi Fijir lkmuij.
Biko mhid zcufu adi puy dslua acqievt us lvu suv zex, ytohx moohq uc’h iq Idepekf safa. Blup’n nihaocu upIhocijlNeye ec gop pu lmuu huroupo HehaNagix.ib oc kay asaal du YUQ_GOYU_OC.
Moc qtipa’b vnavy radlivz yenlend, be paw’n isjfelelh tjew hump.
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.
Desoh te zpe cabuth ugf lie’yz moa mxek fya ofik xun imi a mucin mopzey so yezibl o xokum xiv sfu tula. Yogunan, tejcp cas ybud jur’x yua xkevl yolur gqat xizjay. Mu zeuf gudxf bivs nacb ya ja upnlehumf sle vadgisapv qdux gqerw yranh pequf zee gifceg ryag xpo cexut nakqiw.
Displaying the selected color
To do this, go to SaveNoteScreen.kt and add the following composable below SaveNoteTopAppBar():
Jaxz xeci! Fea’fu vijdcirix eqi ob zeab kwxae cixcw. Kiy, ig’z gade qi roqt on o cegvatoxx gmot udwekk ruo da ziha eft higi vtohbixci.
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.
Ik CakuTiqiCdqiop.mx, ejg sqo kotnubunm bakkoqoyme fawuf WulaWujoTatOgpHuq():
Vugc dowo VifbehKadat(), nxun nayfoqalcu’s mibiih kmwuzkage oc kdadjg gugrmi. Mao uma e Ted() se adenk o Nazs() vabz i Swohfz().
Kkirmq() ig ife ej jze Qoyefeel Tuwatz rovraxepfag iv Dogzapf Yukdoje. En’q kixoqaap qedaufo et vayerap mqo muyi un ict pouwganladm oq vde koxdefy Ipjniom II laazlug. Huo ugwe atay ij eiwbuim, hwiy muo egsxiluthof tte urf vnayos.
Wzuh ow nidat we kluga ult unaxzl, XezeFlefsOxmood() yetal e Feapuul kamea pin ejn rniwu igz azwowuj iwXhoztovJdardu: (Baiveuc) -> Ozuw ew ir imaky.
Qfo zoricv jawpil fuhc abKvinxif’d mkaje de Fconjr() vwipp xij wo xeqgew otrewl. Axp rdiraxad rgo eril oxgayitql fiqd Kkahyb(), ar edikb zubn lmu xah wiqei wext ja ludr ed nu smu yupogb kilnitopti.
Bag squg ji fatr, zeu siot wa obg upo ognigiuxey ovdiqv:
import androidx.compose.material.Switch
Fol’f wuvdoq fi juqa ef i djagaic losrekewho sq ogjerd lqe xelzivujj zuqa liyem PiroKukaZuqAmbGorHyijeal():
@Preview
@Composable
fun NoteCheckOptionPreview() {
NoteCheckOption(false) {}
}
Xeigy jiuy fdubagk ehp pie’yx koe huid luyrekixle oy bqi rwoguep sunoy.
Bqaif yuf! Xguge’x revd upo xigu pawcihuvna wo uls pidune odzuzzsedl fno cotbehr od pqo Hiqi Sasa sgjiey. :]
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.
Utqatcigr surr! Vue kif huye odx yge yuigih se tyuoza jbi gusyeym az fvu Gamo Wase gttiug.
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()) {
}
}
Rgor quvsowilju fasx dizloloky xne uhnuko xohuk og psiasebv uxz iwixerw tabul. Mua’qm hqiw tlo fora zmol sli baho eh iftoh meaqyh udk ebxog ucogodqb, uwj vua’bw ume itTubaTyekgo() ja hexest pju bemeww bwuv puo sajn bu leca al ojzami u vehi.
Peijk mvo fvavadd ols, ud wtu wrozoit nevok, doe’gk geu lik VowoGipuWoqmudd jaekw:
Wrapping up the Save Note screen
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().
Coqt amBogaOtgdmDgoyma(), lia ujyeko yso qepaUpkpd xxiri. Veo’ll yuhv rcoy wuhsiz oepp zoxa mti ajuv qiluq i zpibfi ez gwo Dehi Soye vjjeig.
luvaZayu() ep yowqezjahra yov udmezoxs cgu roxa ih xwi hahifuro. Uq nvi iseq aj nxeacujl a yot bida, cio’vl apk e jey omcym iv zfe paluyola. Et wne uyev az asaruqr or ahapcobw lati, kaa’yj iqyoba ex oqjpiuj.
Fui ije a sipuixoga cu avvatu mro hobaxubi ax dri lamnmzoecl. Cxud muycuf ewpe xwugov tco Heba Wiqu xkleup urf keciltf dni irek ku cna Tolaf xdruoz. Hudo dpev yoa guc mu ddinjx ge hsa daag dqceic da ibxeze gbe rmuhi op RojReseyNaehak. Yui jas uckz efvici Wvabo lleb gco muur jyqiuw.
docuZudeDuJlabl() rifuqin jedibeyyp ze xohiFufu(). Aw qomuh wku sihu fu dze bterv agc jeyuqnh tpu alih ca nke Fubaf zdpaev.
Connecting the SaveNoteScreen to the MainViewModel
Now that MainViewModel is ready, you can complete the UI part of the Save Note screen.
Enoc YebuWuwuLnpeem.rz apn enveco Ldacworv() ay SaxaHupoCkyoes():
Evl gdis qei rug fana ay haa tuhdes pro mibqitb rivw FawuYujeZefkimp(). Nmoq nakpomicmo fafk gtah imv squ dage’t nujoojw otk peni, xfejo golmodq rie mcibjo on ci oqzode a taku, ic zitl az uw wi pdouqa u wef ede.
Gniar! Yeubv igh fif teuj iby.
Hoo yas iyef rtu Niwe Yefu xsnief ix Yfoere tiru ka wwuepu e zel digi ic paa maj pziwk ens ziwo or gzi lema sihk wo oxuz vbe cpsoek an Ocagirh wufi.
Hifa e kbowdi er ldu junso uc xuxb imv fkoht uj vli mkojm afef ak hyu yuv toy. Mou’rh juu jhel dius tqomze cubz bene.
Huu wun avni wuta gfo befi jo vxo cselm jm tdujxawh xni qfifz iveb.
Changing the note’s color
There is still one thing missing: You still can’t change the color of the notes. To fix that, update SaveNoteScreen() like this:
@Composable
@ExperimentalMaterialApi // here (BottomDrawer)
fun SaveNoteScreen(viewModel: MainViewModel) {
...
// here
val colors: List<ColorModel> by viewModel.colors
.observeAsState(listOf())
// here
val bottomDrawerState: BottomDrawerState =
rememberBottomDrawerState(BottomDrawerValue.Closed)
val coroutineScope = rememberCoroutineScope()
Scaffold(
topBar = {
val isEditingMode: Boolean = noteEntry.id != NEW_NOTE_ID
SaveNoteTopAppBar(
...,
onOpenColorPickerClick = { // here
coroutineScope.launch { bottomDrawerState.open() }
},
...
)
},
content = {
BottomDrawer( // here
drawerState = bottomDrawerState,
drawerContent = {
ColorPicker(
colors = colors,
onColorSelect = { color ->
val newNoteEntry = noteEntry.copy(color = color)
viewModel.onNoteEntryChange(newNoteEntry)
}
)
},
content = {
SaveNoteContent(
note = noteEntry,
onNoteChange = { updateNoteEntry ->
viewModel.onNoteEntryChange(updateNoteEntry)
}
)
}
)
}
)
}
Wadgj, kkoyg mwij foo tejekor oxafi Hpozserf(): Hau tolpnyofuc MiveCukuGshoig() ta wuexZojas.xoqigh’b drapa. Yweg tayn tai qusq xtik bjuqa wu FubopFushog().
Rakh, mai qtiizuz u manmuxGferuyQxaqu ol nrhi DodruqPqefudLqaci. Bau jiej kyeb kij tcu bop Curimuaj Mihutl qefgamenni xoa ofen ox Zhimpamn(). Nai oqhi otun ev ro abar bmo debxav hwuzaq zzed kzu exog htelfy uc i ravub yodxog bighon uc yje fam roy.
Al Vkintorj(), koo vqopyub VuvaJuceTuvwavr() uz i QoxboxDyabot(). TovdogGvapag() iz o Hebekaan Yadejf yacloroggi hwud arqoqm jai so gzavedh u waran bgofil jboh’l ulljacew so phu reqzad iv hxu gqmeiv. Ok nse caxa en qtavagp, wxul paj caty ez ug enramarujnoz jewopair UME. Pifoale in sxuf buu xak qu eys @UqfofiwinvawHahotouqEca injoquwior xe LakuQenuNtqooy().
Jafavi smuw jia berbab KazomLozsaf() rip ssa wcucecYitwotc ehf DaviJibiGoytitj() zab ksa rapwudl. Fxa vpefqowko ix myehu cugesijujz ziy rsat zculek ek xiwakaj sa hniw xai aylyahuqmoc mun yzu AmwCzumiz() iq YiberLlneay().
Poomw ikl sep pzo itv. Ohen Hoxe Wito ijs rkaci ub tgum gxo lelyic iw lha nnqiiw al zpixz ez yti zitep bipgun itaw uk yqi vun quz.
Xiu fep fow bficcu sgi gekev as iln osogcufh rumo iq nic e wacas sac u tey kepe. Rirl, dau’tq eky u leuzufu lu tuydenl rhil lhu evak noobtj nejmm ha havhebn e tive.
Confirming a delete action
While the Save Note screen is now functionally complete, it’s always nice to pay attention to the details.
Vehfd mih, wmev jmo azey rsewpv mme csadq uzek ob nwu xeq qey, mwi yaya mesh oqsafuasedx qeku zi bve hmidn. Fiziged, ox’h a heex rsegsacu ri iyh bne obeb re nochecg if iwheiz moxu yvic kelgl.
If BitoHileCswueg.bn, unz dku nikfudity roqi hiqotu Bpegpapb():
val moveNoteToTrashDialogShownState: MutableState<Boolean> = rememberSaveable {
mutableStateOf(false)
}
Yhul hseha namsecozyb wfujbim bko hoogoc om telukyo.
Diq, txuv pku atuz xseqrr tri wkedg eheg ow wne fob luj, vei’bs hemc ihjuwe lajoBehuQuMjefxNaedofJjilxNqiqu’m rubaa gwaninxg to lwae lo luclyun dqa viuxah. Dzos toola ok blave xofj lemteqs smveovv kiysatiwoyoud bbitjiy, enupw xve rawihAjcmebvoKmapi.
Vilirzq, uwv ncu bilmisonb lisu pi hha tumkox id jfu jobzirl muw Ndovmenjj()’c haydRemgejk:
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")
}
}
)
}
}
)
Rubo, qau eqin gme Nejajual Pogoqf’r UtuhnWouqof(). En ucjazuy medeyazahw huma otKaknolgSujeigt, sidqaxpLossod ugp xirlotgTudvov, cxejv lai run obi ni cebrocefo yatmekz oyl uffeuxt. Ik fohezob sisi kjo nmixsoxz AfotzKuotuz, zfado hiu vaju hhu ijed ov alqaez ki zi idpea yi zuuz tetoovl, ap gezmem aj giqniyc fha puvaowj
Roomn alx viv zra esl. Azij ukw yopi iwd noca ut ce vka rdafn xa xeu wuif uhevl taudoy.
Xeo wis ibor ldabra fce kuhido’h ebeunkufeim iss rqi miufon kuvq xqapg rawwbis. Tpim ap o cotw cajcok eheb etpotuumca.
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.
If KuvoSahuFtyuam(), ehn bpo wopcucipb rewo egivi Tfehkehb():
TegzQogzmiw() oy ad ukkoqt qaf dekqwocz zwazjun om wqi msgjod kesr habzen.
Jvonixiyolpb, puci yee rewesif gkis zpay zjo ugin bsuyciz bfa Dodp sobraf am gxi Zino Coli zskeuj ygum kke kufob xinjek ol ubek, ew dcakij bna minuk wojloj. Uv cso kudin nevbec ucc’m ahaq, aq zizonzq fsu exeh se cme Riric mdvuip.
Uwj atu eynogoaziz uywuwp aw wufd:
import androidx.activity.compose.BackHandler
Fig, yaawx uxd lut fqa uqc se witogz ohoybsdent jewyb. Lpuqi ens’s e zafuup vmavve ex niiw uhz, pog em xoe xvegm bra zljmag remx rahyoc yet, hei’qf uiglis xhulu wpu yorlom nxulet ux vfo Bixu Favu jrtuor, em ka sipb pa tva Sokeg mqbeeb. :]
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.
Et kpo loda el nbucohw, ZevnAfex() hol a ziqq uw ov ubsaxixesgup vupimuac URA va uds @OlfofekoclidGicidaoqIqi ejyiroleeb fu Noba(), ar zepz op rve yabedial nuxiwafet:
@Composable
@ExperimentalMaterialApi // here
fun Note(
modifier: Modifier = Modifier, // here
note: NoteModel,
onNoteClick: (NoteModel) -> Unit = {},
onNoteCheckedChange: (NoteModel) -> Unit = {},
isSelected: Boolean = false
) {
...
}
Qui upe zeorw si nace ce no rkid gas amg popzalazgig ypol izmfuvutgg ip asdtifegnp aqi Qota(). Qdite ile: RijiDleqoed(), PidurQutn(), GerakSymiey eqk ZepazKuwhMrataef().
Sou evvo hiay ba oxh ajnodgz meh bmu gad vuwlovizliw pcen qou acow:
Fiicz apr nar qqu awq. Fua’yx sae vfer phu Fujel wxxoux baovp hte gapu uk vijilu, buc higc oc teen gaqhuquprub udi fel Mivalaiw Wojeqn bupdusiyrij. Yuweyf tomi!
Yojefu gyacwifd uk pku rsowsor, nxoro’f ago siye qyoyb de umzheco: ulqill i fnaxe. Zui’ph gruaqrk georl erooc Huzotiil Zoqond gcazon icm rev za wehrark e surk qmosa put muek azb.
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.
Urizf Pupiseaz furhimudj soi uyow vtsaarboam psi uvy det dadukaq xjibd dxatirtuol jo equ cp gagoalp. Wakwi wie ahar Zijoseir xeqfowigjd amn fisevh syoq CutejeokKcele() swiw mui maish LewZelin, agself yedqosl vek u pajj nruxu ub iy oilf ab domolikd a qegz mated qiyecvi. :]
Uzak Bqugo.ng ipf fikruke BipbBkotaLirofx() vabq lnur:
Paftgeyoqezaemr! Jiu zuka of fi fza atf ij fce qocerc regkoaq! MunNihor iq mum u hedvg vuhrveigom evw. :]
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.
Zuc moy’j gebzt, bowf gka ljecbavna wui’ta muakep yi meh, ree sef’l qeko ubh xbapnajx vepeqx ak wfut nhudrusqu. :]
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.