rememberCoroutineScope
is a composable function that returns a CoroutineScope bound to the point of the Composition where it's called. The scope will be cancelled when the call leaves the Composition. If you create your own coroutineScope instead of remember you might get MonotonicFrameClock is not available in this CoroutineContext
error as in question here.
LaunchedEffect
is a remember under the hood with coroutineScope which runs when it enters composition and when any of its keys change.
@Composable
@NonRestartableComposable
@OptIn(InternalComposeApi::class)
fun LaunchedEffect(
key1: Any?,
block: suspend CoroutineScope.() -> Unit
) {
val applyContext = currentComposer.applyCoroutineContext
remember(key1) { LaunchedEffectImpl(applyContext, block) }
}
LaunchedEffect is good for launching animations, snackbar, scolls any default suspending function Compose has also another very useful usages is
triggering some events without user interaction
for instance executing a callback only when reaching a certain state without user interactions as in this question
LaunchedEffect(statementsByYear != null) {
if (statementsByYear != null) {
onDataAcquired(statementsByYear!!)
}
}
LaunchedEffect gets triggered on composition but as in question since value is null if block condition is not met then when statementsByYear
is set by api statementsByYear != null
changes from true to false and if block condition is met and you run the statement.
Or only once inside a Composable using help of ViewModel saving a flag as in this question.
block of LaunchedEffect(keys) is invoked on composition and when any
keys change. If you set keys from your ViewModel this LaunchedEffect
will be launched and you can create a conditional block that checks
same flag to be true that is contained in ViewModel
LaunchedEffect(mViewModel.isLaunched) {
if(!mViewModel.isLaunched) {
mViewMode.iniBilling(context as Activity)
mViewMode.isLaunched = true
}
}
Also conditional blocks enter composition when conditions are met so by wrapping LaunchedEffect with if block you can define when it will enter and exit composition which means canceling job its coroutineScope might be running as in this answer
if (count > 0 && count <5) {
// `LaunchedEffect` will cancel and re-launch if
// `scaffoldState.snackbarHostState` changes
LaunchedEffect(scaffoldState.snackbarHostState) {
// Show snackbar using a coroutine, when the coroutine is cancelled the
// snackbar will automatically dismiss. This coroutine will cancel whenever
// if statement is false, and only start when statement is true
// (due to the above if-check), or if `scaffoldState.snackbarHostState` changes.
scaffoldState.snackbarHostState.showSnackbar("count $count")
}
}
This block will enter composition when count is bigger than 0 and stay in composition while count is less than 5 but since it's LaunchedEffect it will trigger once but if count reaches 5 faster than Snackbar duration Snackbar gets canceled because block leaves composition.
AboutScreen()
and have in there aText()
that shows the number of seconds since the app was built, and you use a coroutine to update the state for thatText()
. The user visits the about screen, then leaves and uses other portions of your app for an hour. – OscillogramCoroutineScope
that is scoped to the relevant composable, and that would berememberCoroutineScope()
in that composable. If for some reason you do need that coroutine to keep running, you need a scope that matches the desired lifetime. This is no different than any other coroutine scope decision: it's not about what's easy, but what is the proper lifetime for the coroutine to execute. – OscillogramLaunchedEffect
side of matters for that. – Oscillogram