Pablo
04/08/2025, 5:49 PMwhile(true)
in viewmodel
init block (inside a viewModelScope.launch
corutine) for updating the uiState values each 100ms? I need to do it each 100ms because it's data that is being varied very fast. The issue is that I'm not completly sure if that while(true) is attached to viewmodel lifecicle and will die when the viewmodel dies, for example. The values required on the UiState doesn't come from a flow, so I tryed doing this while true with a delay of 100ms as a simple way of update them.Pablichjenkov
04/08/2025, 6:32 PMwhile (true) {...}
is never a good practice, no exceptionPablichjenkov
04/08/2025, 6:33 PMWhile (isActive) {}
Semantically cleanerPablo
04/08/2025, 7:38 PMChrimaeon
04/08/2025, 7:42 PMPablichjenkov
04/08/2025, 7:43 PMPablichjenkov
04/08/2025, 7:46 PMPablo
04/08/2025, 7:46 PMPablo
04/08/2025, 7:47 PMPablo
04/08/2025, 7:47 PMPablichjenkov
04/08/2025, 7:47 PMvar isActive = true
And set it to isActive = false in onClear but that's normally not neededPablichjenkov
04/08/2025, 7:49 PMPablo
04/08/2025, 8:04 PMPablo
04/08/2025, 8:04 PMPablichjenkov
04/08/2025, 8:07 PMPablo
04/08/2025, 8:08 PMPablo
04/08/2025, 8:08 PMMichael Krussel
04/08/2025, 9:03 PMStylianos Gakis
04/08/2025, 9:24 PMStylianos Gakis
04/08/2025, 9:32 PMPablo
04/09/2025, 7:39 AMclass MyViewModel : ViewModel() {
// Replace with a flow from your repository
val sourceOfTruth = flow {
delay(1000)
emit("loaded data")
}
val state = sourceOfTruth.stateIn(
viewModelScope,
WhileSubscribed(5000)
"initial"
)
}
I don't have a repository, and I don't have a flow, I'm reading some values with reflection from various origins. And I'm reading them on a while like this:
class ScreenViewModel(
private val specificationsUseCase: SpecificationsUseCase
): ViewModel() {
private val _uiState = MutableStateFlow(SpecificationsScreenUiState(loading = true))
val uiState: StateFlow<SpecificationsScreenUiState> = _uiState
init {
updateUiState()
}
private fun updateUiState() {
viewModelScope.launch {
while (isActive) {
_uiState.update { currentState ->
currentState.copy(
pageContent = specificationsUseCase.getInfo(),
loading = false
)
}
delay(100)
}
}
}
}
Pablo
04/09/2025, 7:46 AMPablo
04/09/2025, 7:46 AMStylianos Gakis
04/09/2025, 8:30 AMclass ScreenViewModel(
private val specificationsUseCase: SpecificationsUseCase
): ViewModel() {
val uiState: StateFlow<SpecificationsScreenUiState> = flow {
while (currentCoroutineContext().isActive) {
val info = specificationsUseCase.getInfo()
emit(SpecificationsScreenUiState(pageContent = info, loading = false))
delay(100)
}
}.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5.seconds),
SpecificationsScreenUiState(loading = true),
)
}
And on your call site you need to make sure to call
viewmodel.uiState.collectAsStateWithLifecycle()
instead of just collectAsState
Pablo
04/09/2025, 3:21 PMPablo
04/09/2025, 3:22 PMdata class SpecificationsScreenUiState(
val loading: Boolean = false,
val pagerState: PagerState = PagerState( pageCount = { Page.entries.size } ),
val pageOneContent: Map<String, String> = mapOf(),
val pageTwoContent: Map<String, String> = mapOf(),
)
class SpecificationsScreenViewModel(
private val specificationsUseCase: SpecificationsUseCase
): ViewModel() {
private val _uiState = MutableStateFlow(SpecificationsScreenUiState(loading = true))
val uiState: StateFlow<SpecificationsScreenUiState> = _uiState
init {
updateUiState()
}
private fun updateUiState() {
viewModelScope.launch {
while (isActive) {
Log.d("XXXX", "viewmodel while (isActive) loop")
_uiState.update { currentState ->
val currentPage = Page.entries[currentState.pagerState.currentPage]
currentState.copy(
pageOneContent= if (currentPage == Page.ONE) specificationsUseCase.getInfo1() else currentState.systemPageContent,
pageTwoContent= if (currentPage == Page.TWO) specificationsUseCase.getInfo2() else currentState.cpuPageContent,
loading = false
)
}
delay(100)
}
}
}
}
Pablo
04/09/2025, 3:24 PMval uiState: StateFlow<SpecificationsScreenUiState> = flow {
while (currentCoroutineContext().isActive) {
val currentPage = Page.entries[currentState.pagerState.currentPage]
emit(
SpecificationsScreenUiState(
pageOneContent= if (currentPage == Page.ONE) specificationsUseCase.getInfo1() else mapOf(),
pageTwoContent= if (currentPage == Page.TWO) specificationsUseCase.getInfo2() else mapOf(),
loading = false
)
)
delay(100)
}
}.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5.seconds),
SpecificationsScreenUiState(loading = true)
)
but I can't get the currentPage, because I don't have currentState
to get currentState.pagerState.currentPage
Pablo
04/09/2025, 3:28 PMPablo
04/09/2025, 3:31 PMPablo
04/09/2025, 3:36 PMDisposableEffect(Unit) {
viewModel.setScreenVisible(true)
onDispose {
viewModel.setScreenVisible(false)
}
}
Michael Krussel
04/09/2025, 3:39 PMPablo
04/09/2025, 3:41 PMPablo
04/09/2025, 3:42 PMPablo
04/09/2025, 3:44 PMPablo
04/09/2025, 3:53 PMPablo
04/09/2025, 4:41 PMfun onResume() {
_uiState.update { currentState -> currentState.copy(onScreen = true) }
updateUiState()
}
fun onPause() {
_uiState.update { currentState -> currentState.copy(onScreen = false) }
}
the loop on the updateUiState method now checks that onScreen val of the uiState:
private fun updateUiState() {
viewModelScope.launch {
while (isActive && _uiState.value.onScreen) {
//JOB
delay(100)
}
}
}
and I set that onScreen value in the screen composable with this:
LifecycleResumeEffect(Unit) {
vm.onResume()
onPauseOrDispose {
vm.onPause()
}
}
Pablo
04/09/2025, 4:43 PM