a

    Arpit Shukla

    10 months ago
    How to pass events to Composables? I have a composable
    C1
    which collects an event Flow from a view model.
    C1
    calls another composable
    C2
    passing some data and lambdas.
    C2
    contains a Scaffold. Now when my ViewModel sends an event of showing a snackbar (after doing some validation when user clicked a button), I want to pass that event to
    C2
    . How can I do this?
    Zoltan Demant

    Zoltan Demant

    10 months ago
    I dont know if this is the correct approach, but in my case I solved it by having a 'prompt' in my state. So, user presses a button, something happens, then a prompt is added to the state, and the below code receives it. I use
    collectLatest
    so that a new prompt overrides the previous one right away, but you can use collect if youd like to show them one by one. The rememberUpdatedState is just so that the same LaunchedEffect block runs continiously, instead of a new one spinning up everytime a new prompt is available. The
    collectLatest
    shows the snackbar, and notifies my viewmodel that the prompt has been consumed so that the state is cleared of it.
    val updatedPrompt by rememberUpdatedState(prompt)
    
    LaunchedEffect(Unit) {
        snapshotFlow { updatedPrompt }
            .filterNotNull()
            .collectLatest { current ->
                when (current) {
                    is SomeMessage -> {
                    // Show snackbar, notify to clear prompt
                    }
                }
            }
    }
    a

    Arpit Shukla

    10 months ago
    Is this the code for
    C1
    or
    C2
    (composables in my question)?
    Zoltan Demant

    Zoltan Demant

    10 months ago
    Wherever you want to consume & show the snackbar 🙂
    a

    Arpit Shukla

    10 months ago
    I receive the event in one composable
    C1
    and have to show the snackbar from another composable
    C2
    .
    Zoltan Demant

    Zoltan Demant

    10 months ago
    You probably need to pass in the data to
    C2
    somehow, in my example I would pass in the prompt. You can then use something like my code above to consume it and actually show the snackbar!
    Albert Chang

    Albert Chang

    10 months ago
    I think it’s better to hoist your state.
    @Composable
    fun C1(eventFlow: Flow<String>) {
        val scaffoldState = rememberScaffoldState()
        LaunchedEffect(eventFlow, scaffoldState) {
            eventFlow.collectLatest { message ->
                scaffoldState.snackbarHostState.showSnackbar(message)
            }
        }
    
        C2(scaffoldState = scaffoldState)
    }
    
    @Composable
    fun C2(scaffoldState: ScaffoldState) {
        Scaffold(scaffoldState = scaffoldState) {
            // content
        }
    }
    Adam Powell

    Adam Powell

    10 months ago
    Or even go one further and hoist the snackbar state all the way into your ViewModel so you don't have to feed it from an event observer, you can just show snacks directly there.
    a

    Arpit Shukla

    10 months ago
    @Adam Powell If I hoist this state from my view model, I will also have to expose a lambda to change this state when the snackbar disappears after the timeout. Is that correct? Also I want my snackbar to disappear after a config change, or just be visible for the remaining duration only. But if I hoist it like this and there is a config change before the last snackbar disappeared, another snackbar will appear and will stay for another 4 seconds. How can I fix that?
    Adam Powell

    Adam Powell

    10 months ago
    SnackbarState
    is defined to allow for this; the snackbar host will dismiss the specific message after the timeout. In terms of sticking around longer, arguably that's not a problem. After the screen reconfigures in a config change the user is often a bit disoriented anyway, a couple seconds more of seeing a message that they might otherwise miss isn't necessarily a negative experience. Something you could handle the timeout in a ViewModel scope or something with instead if you were so inclined but I wouldn't bother in my own code.
    a

    Arpit Shukla

    8 months ago
    If I put my
    SnackbarHostState
    in the ViewModel and display snackbars directly from there, how can I access string resources from inside the ViewModel?