Jhonatan Sabadi
02/16/2024, 7:48 PMval state: StateFlow<String> = stateFlow(initialValue = "Loading...") {
val result = getData("state")
getAnotherData(result)
}
private fun <T> stateFlow(
initialValue: T,
dispatcher: CoroutineDispatcher = <http://Dispatchers.IO|Dispatchers.IO>,
callback: suspend CoroutineScope.() -> T,
): StateFlow<T> {
val scope = CoroutineScope(SupervisorJob() + dispatcher)
val stateFlow = MutableStateFlow(initialValue)
scope.launch(dispatcher) {
stateFlow.update { callback() }
}
return stateFlow.asStateFlow()
}
The idea is to “clone” liveData
builder,kevin.cianfarini
02/16/2024, 7:55 PM<http://Dispatchers.IO|Dispatchers.IO>
.kevin.cianfarini
02/16/2024, 7:57 PMcallbackFlow { … }.stateIn(scope, initial)
would be able to invoke a cleanup when the channel gets cancelled and thus unregister your callback.Jhonatan Sabadi
02/16/2024, 8:04 PMViewModel.stateFlow
to use viewModelScope
to launch coroutineskevin.cianfarini
02/16/2024, 8:08 PMviewModelScope
is now discouraged by Google because is has a static dependency on Dispatchers.Main
and makes testing impossible without Dispatchers.setMain
and Dispctahcers.resetMain
.kevin.cianfarini
02/16/2024, 8:09 PM<http://Dispcatchers.IO|Dispcatchers.IO>
.kevin.cianfarini
02/16/2024, 8:14 PMkevin.cianfarini
02/16/2024, 8:15 PMprivate fun <T> stateFlow(
initial: T,
scope: CoroutineScope,
callback: ProducerScope<T>.() -> Unit
): StateFlow<T> = callbackFlow(callback).stateIn(scope, initial)
But at this point the builder isn’t very useful when you could just call those two functions manuallyJhonatan Sabadi
02/16/2024, 8:22 PMval state2: StateFlow<String> = flow<String> {
val result = getData("state")
getAnotherData(result)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),
initialValue = "Loading..."
)
Jhonatan Sabadi
02/16/2024, 8:33 PMkevin.cianfarini
02/16/2024, 10:18 PMlouiscad
02/17/2024, 1:48 AM