How to use SharedFlow in Jetpack Compose
Asked Answered
O

3

7

With state flow I can use

val items by myViewModel.items.collectAsState()

I suppose shared flow cannot be used this way. Are shared flows even applicable for Compose?

Otherness answered 26/3, 2022 at 2:38 Comment(0)
R
4

Technically you can collect it as state as any other Flow - with an initial value:

flow.collectAsState(initial = 0)

This state will have the last value emitted by the flow during the time of view being presented, or the initial value. I'm not sure this makes much sense, though.

But you can also use it as a way to deliver events that require a one-time response, as shown in this answer.

Rotate answered 26/3, 2022 at 4:8 Comment(3)
Can we collect this Flow from inside another ViewModel?Ainslee
@Ainslee I'm not sure I got the question. technically you can pass flow to an other view model function, and collect it there using viewModelScope, but I'd say it's cleaner to use some other class with shared scope between your view models, e.g. repositoryRotate
With this approach you won't receive updates if state is the same. For instance: user press the button and you want to show snackbar or toast with sharedflow data.Alterable
S
17

SharedFlow should be used for one-shot events(navigation, toast, etc... ).

So this is the way to collect a SharedFlow:

@Composable
fun <T> Flow<T>.collectAsEffect(
    context: CoroutineContext = EmptyCoroutineContext,
    block: (T) -> Unit
) {
    LaunchedEffect(key1 = Unit) {
        onEach(block).flowOn(context).launchIn(this)
    }
}
Spineless answered 26/3, 2022 at 7:29 Comment(2)
What's a real code example of how to call this in Compose?Ainslee
Modelling events using a once-off-mechanism usually is an antipattern: medium.com/androiddevelopers/…Levulose
R
4

Technically you can collect it as state as any other Flow - with an initial value:

flow.collectAsState(initial = 0)

This state will have the last value emitted by the flow during the time of view being presented, or the initial value. I'm not sure this makes much sense, though.

But you can also use it as a way to deliver events that require a one-time response, as shown in this answer.

Rotate answered 26/3, 2022 at 4:8 Comment(3)
Can we collect this Flow from inside another ViewModel?Ainslee
@Ainslee I'm not sure I got the question. technically you can pass flow to an other view model function, and collect it there using viewModelScope, but I'd say it's cleaner to use some other class with shared scope between your view models, e.g. repositoryRotate
With this approach you won't receive updates if state is the same. For instance: user press the button and you want to show snackbar or toast with sharedflow data.Alterable
R
3

Just use LaunchedEffect with repeatOnLifecycle. Without repeatOnLifecycle, your flow will be collected even when the app is in the background.

Eg.

@Composable
fun YourFunction() {
    val lifecycleOwner = LocalLifecycleOwner.current
    LaunchedEffect(Unit) {
        repeatOnLifecycle(Lifecycle.State.STARTED) {
            withContext(Dispatchers.Main.immediate) {
                sharedFlow
                   .collect { /* doSomething */ }
            }
        }
    }
}

Anyway, I suggest using channels instead of shared flows, if the flow is collected in only one place as it has built-in buffer so you will not miss any event.

You can find more explanation in Philipp Lackner video about One-Time Events in Jetpack Compose

Retool answered 21/12, 2023 at 8:14 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.