When collecting a `SharedFlow` (application level ...
# compose
m
When collecting a
SharedFlow
(application level coroutine scope with replay = 1) in compose using
collectAsStateWithLifecycle
(initialValue = null), I notice that on screen rotation the first value is
null
, but shouldn’t it be
sharedFlow.replayCache.firstOrNull()
? The reason I ask, is because scroll position is being lost on rotation, but when I use ``replayCache.firstOrNull()`` as the
initialValue
everything works as expected.
c
When you provide the initialValue to collectAsStateWithLifecycle you'll eventually arrive at
Copy code
return produceState(initialValue, this, lifecycle, minActiveState, context) {
        lifecycle.repeatOnLifecycle(minActiveState) {
            if (context == EmptyCoroutineContext) {
                this@collectAsStateWithLifecycle.collect { this@produceState.value = it }
            } else withContext(context) {
                this@collectAsStateWithLifecycle.collect { this@produceState.value = it }
            }
        }
    }
which passes null into produce, setting its value to null before collecting from the shared flow. It would then get the cached value from the shared flow in the collect block, but by then you've collected the null value. Notice the overload of
collectAsStateWithLifecycle
that extends
StateFlow
just calls
.collectAsStateWithLifecycle(initialValue = this.value, ...)
which is pretty much the same thing you're doing when you are explicitly providing the initial value.
m
Thanks Caleb. So I think the common sense solution here would be to convert my shared flow to a state flow in the view model and collect that instead. And indeed, using the viewModelScope for that conversion, seems to work well. I think my initial confusion was from knowing that a SharedFlow will emit from the replayCache first, when collected. However, as you pointed out,
collectAsStateWithLifecycle
provides the passed in
initialValue
first.
🙌 1
w
Yeah, in my mind Compose should really only collect StateFlows and not SharedFlow. Compose requires an initial value, and the value of hiding the synchronous value by using Shared isn't meaningful because Compose explicitly needs the most recent value. I think you could argue this is a feature request though and there should be a
SharedFlow
specific extension which checks the replay cache. 🤔
m
For a
SharedFlow
you still need to provide an initialValue to cover the case where the
replayCache
is empty. And so in that case it’s more like the Flow extension fun than the StateFlow extension fun. Also, I guess a SharedFlow is often passed around as a Flow anyway.