Pablo
01/14/2025, 7:07 PMPablo
01/14/2025, 7:07 PMsealed interface MainScreenUiState {
object Loading : MainScreenUiState
data class Error(val throwable: Throwable) : MainScreenUiState
data class Success(
val busStopsServerReady: Boolean,
val busStopsDataAmount: Int,
) : MainScreenUiState
}
Which is initialized this way:
init {
viewModelScope.launch(Dispatchers.IO) {
try{
// sources status
val busStopsServerReady = networkBusRepository.getStops().isNotEmpty()
val busStopsDataAmount = defaultMainBusRepository.getBusStops().size
// update ui state
_uiState.value = MainScreenUiState.Success(
busStopsServerReady = busStopsServerReady,
busStopsDataAmount = busStopsDataAmount
)
} catch (e: Throwable) {
_uiState.value = MainScreenUiState.Error(e)
}
}
}
Pablo
01/14/2025, 7:08 PMfun updateUiState(value: Int) {
//todo _uiState.busStopsDataAmount = value
}
Seri
01/14/2025, 7:10 PMMainScreenUiState
is immutable, meaning instances of it cannot be changed after they are createdSeri
01/14/2025, 7:11 PM.copy(...)
method here https://kotlinlang.org/docs/data-classes.html#copyingPablo
01/14/2025, 7:12 PMPablo
01/14/2025, 7:12 PMSeri
01/14/2025, 7:13 PM_uiState
defined?Pablo
01/14/2025, 7:17 PMprivate val _uiState = MutableStateFlow<MainScreenUiState>(MainScreenUiState.Loading)
val uiState: StateFlow<MainScreenUiState> = _uiState
Michael Krussel
01/14/2025, 7:19 PM_uiState.value
. So the normal way to update a single property in the state object is to use the copy
function. But since you have a sealed interface for the state, not every class in the sealed hierarchy has the property you want to update. My preferred approach to this is to add state updating functions in the interface. Basically have a function for each event that can change the state, and then each implementor of the interface, it returns a new state object based on the logic for what should happen for that event when you are in the given state. If the event is not valid for the current state, then that state could make it a no op and return itself, throw an exception, or go to an error state, based on your needPablo
01/14/2025, 7:20 PMMichael Krussel
01/14/2025, 7:21 PMupdateUiState
do if the current state is Loading
. Should it create a Success
state with a default busStopsServerReady
?Michael Krussel
01/14/2025, 7:24 PMsealed interface MainScreenUiState {
fun updateState(value: Int): MainScreenUiState
object Loading : MainScreenUiState {
fun updateState(value: Int): MainScreenUiState = Success(true, value)
}
data class Error(val throwable: Throwable) : MainScreenUiState {
fun updateState(value: Int): MainScreenUiState = this
}
data class Success(
val busStopsServerReady: Boolean,
val busStopsDataAmount: Int,
) : MainScreenUiState {
fun updateState(value: Int): MainScreenUiState = copy(busStopDataAmount = value)
}
}
fun updateUiState(value: Int) {
//todo _uiState.value = _uiState.value.updateState(value)
}
Pablo
01/14/2025, 7:32 PMPablo
01/14/2025, 7:32 PMPablo
01/14/2025, 7:32 PMPablo
01/14/2025, 7:32 PMMichael Krussel
01/14/2025, 7:33 PMupdateUiState
function, but polymorphism is generally considered better and simpler then doing a type check.Pablo
01/14/2025, 7:34 PMMichael Krussel
01/14/2025, 7:34 PMfun updateUiState(value: Int) {
val currentValue = _uiState.value as? Success ?: return
ui_State.value = currentValue.copy(busStopDataAmount = value)
}
Seri
01/14/2025, 7:37 PMMutableStateFlow
has its own update function for convenience
https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-mutable-state-flow/#1996476265%2FFu[…]ns%2F1975948010
e.g.
class CounterModel {
private val _counter = MutableStateFlow(0) // private mutable state flow
val counter = _counter.asStateFlow() // publicly exposed as read-only state flow
fun inc() {
_counter.update { count -> count + 1 } // atomic, safe for concurrent use
}
}
Pablo
01/14/2025, 7:44 PMSeri
01/14/2025, 7:45 PMStateFlow
is being used here, and how it's a container for the underlying dataSeri
01/14/2025, 7:46 PMPablo
01/14/2025, 7:48 PMPablo
01/14/2025, 7:49 PMPablo
01/14/2025, 7:49 PMPablo
01/14/2025, 7:55 PMif (_uiState.value is MainScreenUiState.Success)
_uiState.value = (_uiState.value as MainScreenUiState.Success).copy(busStopsDataAmount = 1)
I don't like... but seems to be the simpler way, its very similar to the one propossed by Michael I think