Still new to compose + kotlin, so I'm having some trouble getting stateflow working. There might be some fundamentals that I don't understand or missing some function.
Problem: I have two stateflows in my view model and both would trigger a recomposition of the other one.
ViewModel:
private val _networkUiState = MutableStateFlow<NetworkUIState>(NetworkUIState.Empty)
val networkUIState: StateFlow<NetworkUIState> = _networkUiState.asStateFlow()
private val _uiState = MutableStateFlow<UIState>(UIState.Empty)
val uiState: StateFlow<UIState> = _uiState.asStateFlow()
fun someAPICall(
) {
_networkUiState.value = NetworkUIState.Loading
networkJob = CoroutineScope(Dispatchers.IO + exceptionHandler).launch {
try {
val response = repo.apiCall()
withContext(Dispatchers.Main) {
_networkUiState.value = NetworkUIState.Loaded
_uiState.value = UIState.DoSomething(response)
}
} catch (error: NetworkException) {
_networkUiState.value = NetworkUIState.NetworkError(error)
}
}
}
//exceptionHandler calls _networkUIState.value = NetworkUIState.NetworkError(some error) as well
Compose:
val networkUIState = viewModel.networkUIState.collectAsState().value
val uiState = viewModel.uiState.collectAsState().value
Column() {
//...
//UI code
when (uiState) {
is UIState.DoSomething -> {
//read uiState.response and do something
}
is UIState.DoAnotherThing -> {
//read response and do something else
}
}
when (networkUIState) {
is NetworkUIState.Error -> {
//display network error
}
is NetworkUIState.Loading -> {
//display loading dialog
}
else -> {}
}
}
What happens:
1st time calling api:
- NetworkUIState triggers loading (display loading)
- NetworkUIState triggers loaded (hides loading)
- uiState triggers DoSomething with response data
2nd time calling api:
- NetworkUIState triggers loading (display loading)
- uiState triggers DoSomething with response data (from last call)
- NetworkUIState triggers loaded (hides loading)
- uiState triggers DoSomething with response data (new data)
I understand this is because of the recomposition of NetworkUiState before UIState but UIState still has the old value. My question is how can I avoid this when I absolutely need to separate these 2 states at least in the view model?