Hey, I have a question regarding flows: I have a s...
# coroutines
j
Hey, I have a question regarding flows: I have a state flow emitting upload progress and in the end, the upload url. Is there a way to like cancel this flow after it emitted the upload url, or should I use another flow type? Because right now collecting the flow blocks the coroutine forever even though the upload is done
c
A StateFlow might not be the best choice here. StateFlows by design will never complete, so a call to
stateFlow.collect { }
will suspend forever until the collector’s coroutineScope is cancelled. A regular cold flow might be able to better capture the intent of this flow, especially if you only have 1 collector and don’t need the upload URL again after it’s used That said, you can use
transformWhile
to add this kind of “stopping” behavior on top of a StateFlow, but the result is a
Flow
not
StateFlow
.
Copy code
public sealed class ResourceLoadingStatus {
    public data class InProgress(val progressPercentage: Float) : ResourceLoadingStatus()
    public data class Loaded(val url: String) : ResourceLoadingStatus()
}

private val _stateFlow = MutableStateFlow<ResourceLoadingStatus>(ResourceLoadingStatus.InProgress(0f))

public suspend fun loadValue(): Flow<ResourceLoadingStatus> {
    return _stateFlow
        .asStateFlow()
        .transformWhile {
            // always emit the value downstream
            emit(it)

            // after emitting each value, check if we should continue processing more items
            val shouldStopProcessing = when (it) {
                is ResourceLoadingStatus.InProgress -> false
                is ResourceLoadingStatus.Loaded -> false
            }
            shouldStopProcessing
        }
}
r
I would recommend using the
flowOf
builder to make your flow cold. That way the terminal event is reaching the end of the builder block.