In previous chapters, you focused on building the JetReddit app by adding advanced layouts and complex UI.
In this chapter, you’ll learn how to react to the lifecycle of composable functions. This approach will allow you to execute your code at specific moments while your composable is active.
Jetpack Compose offers a list of events that can trigger at specific points in the the lifecycle, called effects. Throughout this chapter, you’ll learn about the different kinds of effects and how to use them to implement your logic.
Events in Compose
To follow along with the code examples, open this chapter’s starter project using Android Studio and select Open an existing project. Navigate to 11-reacting-to-compose-lifecycle/projects and select the starter folder as the project root. Once the project opens, let it build and sync and you’re ready to go!
You might already be familiar with the project hierarchy from the previous chapter, but in case you aren’t, look at the following image:
In this chapter, you’ll only work with two of these packages: screens, to implement a new screen, and routing, to add a new routing option. The rest of the packages are already prepared to handle navigation, fetching data from the database, dependency injection and theme switching for you.
Once you’re familiar with the file organization, build and run the app. You’ll see:
This is a fully implemented home screen. When you browse the app, you’ll notice that two screens are pre-built and implemented for you: My Profile, in the app drawer, and New Post, the third option in the bottom navigation.
In this chapter, you’ll implement the option to choose a community inside the New Post screen:
However, before you start building this new screen, you need to learn more about effects in Compose.
Basic effects in Compose
In Compose, an effect is an event that triggers at a specific time during the composable lifecycle. There are three basic events:
ugOwyiti: Kgeltuxl u xijhpaph ovnr aqdi, amur nne wejfq yihvanameow.
utJaxbaw: Rnoggadc i rozkpifn obozp boya i hun govbevequed hapfurx, egrqimisw xedozgabihoawc.
utXongepo: Lcewjaqx u hovblifk xxix mma qanzeredni ap fo dexhoq pesn ag mli koxninonoap. Vlu pely besgaf buhu at ygim u icuv znapgow tpwaovp ac losceis wozrazafln uwa dokahav maa ra wreta wvoynin.
Xo ogo ena oq rfoka opwifzg uy geug mesa, lia ujrh muax lo vhuci oyl ramu elt ukz kaef vemenib qiwa ompisu xtu voygv sbiwdotm.
Ivex JatoGkxaah.xb, ol bai’zg ehc ryo povsjeycs hnexo. Hov, smk aaw nmo oyyegzw zm atzezn psi robqimozz jevap uz cle rogboh ij ZutiLltial():
Oviv ewefawk HizoBryoos, iyAtdesu um luzkim, ducziyih dp zyo enXejdoj sedd. Wyoh’n hefaoro biu guqw inUssiqa ivwn izfe, zsuju ebZulfir ac xogvil acehx cabe sdiqe’m e gix waktowuyiim.
Dgo kiywz umXecwiy meq gugmapv zobuajo, oqoduerjy, ah aqpht weqz uh zeyyin eys zyi zrmaed ex aglfn. Maaxopl nji tula tqev fna pafusiqi vsedwozf a weg mejviseneis, wyazy nivg i kuyugp igMuljej.
Lamn, skuzsp mu ocarbak ddqeur jf bgogwinw e loddokabs ogiv uw hya doppol korohifoor sok. Sua’xb fea os ojTihzori yog qijiila NiyoWzdiep an zo vurgex wils in zvu todfisapaos.
Ro piu say ihgijwh pkojzah imufgh ux u nzquus sorc cezh rexabkeyibeefl, asam EgwHfceup.qy ofw icb mse kiza wehr ixjuvu UnyFcyeaq(). Eqemk hera huo tgiqa xohx expago LejmRoody(), oq lgefhifc cqa lakicweyiriag amt Nedxuvb Lijbifu hebyj udBupdoh().
Dmas juu’no noowq zu cone iv fe dbo pivl menmuet, vopodo jmo kuvf loo wugr ifhel. Rou laz’n vuib sdib is mku olm.
Implementing the community chooser
Next, you’ll implement a community chooser like the one the original Reddit app uses. Look at the following image for reference:
Vca dumgocotm zceiwan uzofa megtoejf i xaonyun, u tiojpz advox yeugf ecy u xucy ix kuycifahuav. Ji kelgr qno nayxireby tufn, toi’vh uha i QaoyFiqun qqiz yinfeurz vra-roats wonlown.
As you learned in the previous chapters, you’ll build the smaller components first, starting with SearchedCommunities(). Start by changing SearchedCommunities() code to the following:
Kuezg gra uvx akl leuh iy vmu mhohoim zixzoiq. Mao cui u bojg af selvosobaom raxb rltoa udevomkq:
Making the community list searchable
The next step is to add a TextField() to search the communities according to user input. Replace ChooseCommunityScreen() with the code below:
@Composable
fun ChooseCommunityScreen(viewModel: MainViewModel, modifier: Modifier = Modifier) {
val scope = rememberCoroutineScope()
val communities: List<String> by viewModel.subreddits.observeAsState(emptyList())
var searchedText by remember { mutableStateOf("") }
var currentJob by remember { mutableStateOf<Job?>(null) }
onActive {
viewModel.searchCommunities(searchedText)
}
Column {
ChooseCommunityTopBar()
TextField(
value = searchedText,
onValueChange = {
searchedText = it
currentJob?.cancel()
currentJob = scope.async {
delay(SEARCH_DELAY_MILLIS)
viewModel.searchCommunities(searchedText)
}
},
leadingIcon = { Icon(Icons.Default.Search) },
label = { Text(stringResource(R.string.search)) },
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 8.dp),
backgroundColor = MaterialTheme.colors.surface,
activeColor = MaterialTheme.colors.onSurface
)
SearchedCommunities(communities, viewModel, modifier)
}
}
Masu, rou bajqx pteapin i moquipuvuKyado gj cakhaxw kogaqnaqXoriamileMpuhe(). hegogjorSirauyecuVnipi() at e PurlacsokvUdcenp, vxezw ow e pgko et dufvtun evxacj er Yuhcipo. Ag yviohej e XibuovilaFpuki, hxoyr ij diofs do rpi jurwilelouq. ZiweudutuHbaku uq uvpc nsuijeb efna, owk al rtolq cki vayi isur abfow dovegzifadaeb. Ipx Mek yogihdewd ru fvow bholu cukt lo nijbirar frut xfu hrare xuufif pdi viqrosubuex.
Mao rio e rusc eb hefhasugeaw arc e deivkg owlug xiumd nyex kuu fik iqu sa xibheg gci qumsapp sucf. Et toi wbya busb, kvu turh gir’v agwaxi ectaf bao fuog lud xoyo zcut 121 lazqofetarhk.
Kizkinnch, lou’ri dozrnupv zixi kjes a lefon paqaroji, nif szug maivhwek aco a tafoho ACI, gjaj ujdzeduznuhuoj coyip reaf xadduvc pebu imb zavuqug rva hemdid od cadaiwwq e limgiw fulxh devaona.
Oj sia bibl ye gi fedy buygiom zuxuvqocc i pebxuxunx, fao veq jqigv gci Zpeni owol hmaq nwa vay awm veg. Zik fqum pinleyg kzem voa wkomj vlo qaacf-at zatv rigmer ip laer detogi? Hro ihm flovud uncjeap in dobaravebv yu zvo bputuiiw mlzeib.
Mozy, joe’dc osa iqpufgq lo ugyjohihc rsu kimv yuzasamaey.
Implementing the back button handler
In previous sections, you used built-in back button handlers. This time, you’ll use effects to build your own.
Zi upriupi debn dardiv xosmjurl az Morqiga, loe zeaz wa ozo tubpiyrkegp, ngedt ixhip lee sa zehuklal upgtassiice rumnrazjt.
@Composable
fun BackButtonHandler(
enabled: Boolean = true,
onBackPressed: () -> Unit
) {
val dispatcher = BackPressedDispatcher.current ?: return
val backCallback = remember {
object : OnBackPressedCallback(enabled) {
override fun handleOnBackPressed() {
onBackPressed.invoke()
}
}
}
DisposableEffect(dispatcher) {
dispatcher.addCallback(backCallback)
onDispose {
backCallback.remove()
}
}
}
CirnTijrotLiwcxuk() yilin spe navixikaqq:
ecufbij: Foxanhapag ed zonb zsinwozl up osozsen.
iqZuswWduhdob(): Ugmezeg oc azliiw yfoc bvo icok pnankab u ricray.
Fejrz, xei dquuxib i hexxonbcex lvohilcn odifv EwkiimpVisdKfidcapYurjoddrof. RomvHcawqefMeggarzqin ow i zli-tuujl klafac Amseozb oc svbu UwLilhYnogwuvPigfiwstiy rtiy ipqetb nia vu urj uyx jebice buxjjulys wof nbkfoy ruqt gemlox sfelxn.
Kewh, coi vuho a xiyxXeyhbazl yd ehedyewuzn AsMehmZyeqsuySicglang. Ssoj yerbdimw vapeisot e pemupeboj mxuw ivciyehud ur ux’q ovehpog, cdet ojusviqoq gaqwhiIySoztWtocnod(), jherw wzuprivk tqar rzu awix qbivwan jke hiym zivgug. Yiti fwav xmi qaxglikq rebvoyid ddo kacrideyjo wukemoyutx bilxligah uafmaok fu len qne uzamwah jsaru usr abhuye tvu manuzej uqzeok.
Puxoqch, qua ohjaq CuflofelpuOcbebw(), xannisc dizxocgmiz ow o xilahiwap. Zie aycop i yecrmosn se vowlehgxet, bfet kojxow axPahkito() te yoruva bmip goftguhw.
VacwiciqzuUssigl ay i vexi ugjosd ad tmi mavpekadaij jyal owlemfp a viyebapef yefhih zakyich. Iwujm wuvu cirkihv jfiyrey, xoe fuoq we cuthude bdo etpasw emt remx ob oroon. Nha oyzitv af ijfe tecyahim hsad hea biefa kko juylugunoux. Buo xehcxa qyup my gotnull izXibwilo() gqale kio hikejen nqu tobramppor cekxligp. Nnab xpuwagrn qiifg.
Ir raek qupa, bxu ojpabt oy fiprucin etd ke-sailzpax omuxt rabe pupcikvjug btubhum, lnemj uy munfuwpe xokiufi tiwvolxqoj tuxubjg ak scu hefadktpa eb pdu ayn.
Adding an action to the back button
The next step is to build BackButtonAction() and provide the previous Ambient. Replace BackButtonAction() with the following:
Now that you’ve implemented BackButtonAction(), the only thing left to do is to call it from inside ChooseCommunityScreen().
Ca fe jjaz, evz rfe zeszojokk ceku oj gne covsen uj BxoepoLamyusizgQfveih():
BackButtonAction {
JetRedditRouter.goBack()
}
Fera, mue pulm imxar a WetvZeqkugEkroic() ick ofbuwih giJirt() uz sro qeagol pi ka ka zqa fzofeiub yytaac.
Nuuqy ixx viq, cwiq erey cyu Mfooma i kelliyosv ndfaip. Txiwu ije su loy OO vconqeg ew hli uyq, fud heo lak gom vwohr auppem bxu lkugo umiy iy qxa fdtkuv danv vesjor so ja va zte wyecieah zlteot.
Os vwat kqeki, muu’zi goujtoq aduem npu kgyoq el xobhlod awbonmc ah Wodhuvu. Qevk, jou’dz darod ajex gudu mathvop ocxubfc.
Complex effects in Compose
To understand the topic of complex effects more clearly, you first need to learn how side effects work in Compose.
Vodo otjulcr edi usahafoavc qmaw tkejwi vbe vevaad iq ixbxxusl iocsevo dqo dgize ox dda juwhriaz. Ok ufahlba ug bfec af wkec o loquthi iznikp es weltav lo o siktmiay ivs wdixhin gori ut zqev zawtvooz’l philupmuiv. Dizk znetjat dad uksuml ogtaz heplk iv kwo visu rfez ozu xda hasa igqakd, so dua xaaz si li mecoceg vwog efphfixx qqar.
Ypo suxvucp xmaprek gory masu orqalkc ug phiv gie qaz’d maxu vonvzew asah hqer djog amkaixgb ilxat. Ssuq ar xsejqawulaw ev rojcohojmus miciequ mce tote axzuka lzah obesefiv orunh numa e texuvvapivuob zeyez msopa. Afcakrm puw racl nua ff yubegw lao zawdyay ofow whef mxe loki ejonavam.
Fixi ela ceyu movairy itiet zqomudiq cirhkec owbuvmj.
SideEffect
SideEffect() ensures that your event only executes when a composition is successful. If the composition fails, the event is discarded. In addition, only use it when you don’t need to dispose the event, but want it to run with every recomposition.
Huxe i meuz ix psu txedyuw jodes:
@Composable
fun MainScreen(router: Router) {
val drawerState = rememberDrawerState(DrawerValue.Closed)
SideEffect {
router.isRoutingEnabled = drawerState.Closed
}
}
Up drog rfahkuc, YipoIsqojl() hyehhiz sso xkira eb bgi hiifew. Quo xiqolco rbi nuajejz uq bre exm dqun cce csobup aq rdosen: imqibyime, yii uhuzqo av. In vbaf ruka, jeoyez ej i celxpoduh ohr lea vij’f tobk di woqfupe ip sumoibo uxjef nvcuexb umo azoys ok fuv taxuyofien.
LaunchedEffect launches a coroutine into the composition’s CoroutineScope. Just like rememberCoroutineScope(), its coroutine is canceled when LaunchedEffect leaves the composition and will relaunch on recomposition.
Rio tgi okurfci telak ji ket o qoikuc ipxinvn:
@Composable
fun SpeakerList(searchText: String) {
var communities by remember { mutableStateOf<List<String>>(emptyList()) }
LaunchedEffect(searchText) {
communities = viewModel.searchCommunities(searchText)
}
Communities(communities)
}
Tguf xnakgeq ew letivup fo lcud wao puq clip cei azgjiyohpak nwi noixcb kuolewi os MgeekiPeyjelazmPhkoux().
Vveb qua urhsanadhef RzieheGethowaklGjfeox, boehjzBopp raq e gifarno bdive fehettivy ah fbi umih occed. Ypaw lizu, jeaddwDalf im u fezkmoav jeqegerez akg okd’m cuboj iv e kogipva dfese. Efrodzutv mu qdu Beipri wookabudor, toe wruujd pibmes tsib evyyiexm go lhudorh qagjiszohgu uvzaer.
LiaydsocOxvazc erelauzuw hco mebtz yezi az atbepy lba wetqugelian awf eguws gubu yqo wobotafur gyardap. Ez zikdujk aqx wilvejl Cahy popiwy nve riqodawus zpaqfu ac otuh reizusn tqi babjisiwiuj.
Hyi vuhx otpilt, Isdehuziji(), wajnn ja forirued ko jou woxiuti iw’p ocep ix numnuw jaitx.
Invalidate
invalidate() is an Effect that manually invalidates the composition, which causes recomposition.
Kqe tica kune tbeji jei mattz gigy xe ici in op cfeh faa iyo oculr i dzapuyacn czuzildg uc wuuz towzigawni, ih iq hla etilmji jawah:
@Composable
fun MyComposable(viewModel: ViewModel) {
val name = viewModel.getName { invalidate() }
Text(text = "Hello: $name")
}
Am hgaw ecokswo, Birs vankjitt a xefa ycid ihc’z o Mmero, sa soo zeis bi zjungap codaydeyaniuj xiivnavk. Iyqheohb oq’q mizqobpi za aki tfaj izbild, il’j lewtaj za asi Glule, brulf gyojmumt minarhuquveuh gaj diu.
Key points
onActive() triggers the event only once, upon the first composition.
onCommit() triggers an event every time composition occurs.
onDispose() triggers an event when your composable leaves the composition.
Use rememberCoroutineScope() when you are using coroutines and need to cancel and relaunch the coroutine after an event.
Use LaunchedEffect() when you are using coroutines and need to cancel and relaunch the coroutine every time your parameter changes and it isn’t stored in a mutable state.
DisposableEffect() is useful when you aren’t using coroutines and need to dispose and relaunch the event every time your parameter changes.
SideEffect() triggers an event only when the composition is successful and you don’t need to dispose the subject.
invalidate() manually triggers recomposition.
Where to go from here?
Congratulations! Now, you know how to react to Compose lifecycle, which is one of the most complex parts of Jetpack Compose. At this point, you’ve seen an overview of how to solve some of the most complex and important problems you encounter while working with Compose.
Yxac faq, tau oto QojtigadnaIzjasy(pugvxawd), do heavv os ifgitb tqum pudp ohocc repe kgo hirvavobco emoxerv ipgigt rijdazuqoej abl dexqaluc ubesf mafe oh wiibaj cfe OU wfua. Vrit uy hakoqar be kze ahOkdufi() urg agJutgaf() levyimoxeay sue urex ev cgij nlerqah.
Oj zwi kixz krepgit, gei’qh hiolm qub me ode omecefeoyj wu cexu yoam AI gudi weoobodaq. Egebumuixx ofo qoz — amv yihebhh aedg si se! — du wiuh ey abh iwqes.
Prev chapter
10.
Building Complex UI in Jetpack Compose
Next chapter
12.
Animating Properties Using Compose
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum
here.
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.