allan.conda
12/16/2020, 12:34 PMLaunchedEffect(error) {
snackbarState.showSnackbar(error.message)
}
I have this but this doesn’t run if exactly the same error happens.nrobi
12/16/2020, 1:01 PMError
Timo Drick
12/16/2020, 1:11 PMallan.conda
12/16/2020, 1:15 PMallan.conda
12/16/2020, 1:15 PMallan.conda
12/16/2020, 1:16 PMTimo Drick
12/16/2020, 1:16 PMallan.conda
12/16/2020, 1:17 PMTimo Drick
12/16/2020, 1:18 PMclass SnackbarController() {
internal val snackbarList = mutableStateListOf<@Composable SnackbarScope.() -> Unit>()
fun showSnackbar(snackbar: @Composable SnackbarScope.() -> Unit) {
snackbarList.add(0, snackbar)
}
fun removeSnackbar(snackbar: @Composable SnackbarScope.() -> Unit) {
snackbarList.remove(snackbar)
}
}
@Composable
fun SnackbarContainer(modifier: Modifier = Modifier, snackbarController: SnackbarController) {
val list = snackbarController.snackbarList
list.firstOrNull()?.let { snackbar ->
val scope = remember(snackbar) { SnackbarScope(snackbarController, snackbar) }
Box(modifier) {
snackbar(scope)
}
}
}
class SnackbarScope(private val snackbarController: SnackbarController, private val snackbar: @Composable SnackbarScope.() -> Unit) {
fun dismiss() {
snackbarController.removeSnackbar(snackbar)
}
}
Timo Drick
12/16/2020, 1:23 PM@Composable
fun SnackbarUI(modifier: Modifier = Modifier, text: String, actionText: String, onAction: () -> Unit, onDismiss: () -> Unit) {
val scope = rememberCoroutineScope()
val job = scope.launch {
delay(5000)
onDismiss()
}
Snackbar(
modifier = modifier,
text = { Text(text, style = MaterialTheme.typography.button) },
action = {
FaTextButton(onClick = { job.cancel(); onAction() }) { Text(actionText) }
}
)
}
Timo Drick
12/16/2020, 1:24 PMsnackbarController.showSnackbar {
SnackbarUI(
text = "Removed ${item.imageData.displayName ?: ""}\n${item.imageData.category.title}",
actionText = "Undo",
onAction = { dismiss() },
onDismiss = { dismiss() }
)
}
allan.conda
12/16/2020, 1:35 PMI have this but this doesn’t run if exactly the same error happens.^ this is the use case I need to handle
allan.conda
12/16/2020, 1:36 PMTimo Drick
12/16/2020, 1:38 PMAdam Powell
12/16/2020, 3:45 PM@Composable
function:
val job = scope.launch {
we should probably have a lint error for any use of CoroutineScope.launch
or CoroutineScope.async
inside of a composable function. You want LaunchedEffect
instead.allan.conda
12/16/2020, 4:00 PMAdam Powell
12/16/2020, 4:10 PMallan.conda
12/16/2020, 4:30 PMColton Idle
12/16/2020, 4:38 PMAdam Powell
12/16/2020, 5:45 PMTimo Drick
12/17/2020, 12:56 AMAdam Powell
12/17/2020, 2:37 AMLaunchedEffect
leaves the composition entirely, the job will be cancelled.Adam Powell
12/17/2020, 2:41 AMdata class Event<out T>(val content: T) {
var consumed: Boolean = false
Timo Drick
12/17/2020, 2:42 AMAdam Powell
12/17/2020, 2:48 AMAdam Powell
12/17/2020, 2:48 AMTimo Drick
12/17/2020, 2:50 AMTimo Drick
12/17/2020, 2:50 AMAdam Powell
12/17/2020, 2:55 AMAdam Powell
12/17/2020, 2:59 AMAdam Powell
12/17/2020, 3:01 AMEvent
wrappers is kind of like trying to ice skate uphill against the mental model; it's a way to still try to think in terms of each recomposition or run as its own significant event, but model it as state just enough to make things sort of look like they workAdam Powell
12/17/2020, 3:03 AMEvent
wrappers try to circumvent compose's pull towards idempotenceallan.conda
12/17/2020, 3:59 AMallan.conda
12/17/2020, 3:59 AMAdam Powell
12/17/2020, 4:53 AMAdam Powell
12/17/2020, 4:54 AMallan.conda
12/17/2020, 9:06 AM