I'm facing an issue when collecting StateFlow
in Composable
function. Although I checked its type, an exception is thrown indicating that cannot cast.
It runs correctly in the first call to iewModel.fetchFixtures()
, but it throws exception on the next time this method is invoked.
Did I misimplement something?
@Composable
fun Home(
...
) {
...
val uiState by viewModel.fixtureStateFlow.collectAsState()
when (uiState) {
is UiState.Loading -> Box(modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
CircularProgressIndicator()
}
is UiState.Success<List<Fixture>> -> LazyColumn(
modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp)
) {
items((uiState as UiState.Success<List<Fixture>>).data) { fixture -> // line 53
FixtureItem(fixture = fixture) {
println("Clicked")
}
}
}
}
}
ViewModel
@HiltViewModel
class HomeViewModel @Inject constructor(
...
) : ViewModel() {
...
private val _fixturesUiState = MutableStateFlow<UiState<List<Fixture>>>(UiState.Loading)
val fixturesUiState = _fixturesUiState.asStateFlow()
fun fetchFixtures(date: LocalDate) {
viewModelScope.launch(coroutineExceptionHandler) {
_fixturesUiState.value = UiState.Loading
_fixturesUiState.value = UiState.Success(GetFixtures(repository, date).execute())
}
}
}
sealed class UiState<out T> {
object Loading : UiState<Nothing>()
class Success<T>(
val data: T
) : UiState<T>()
}
Stacktrace
FATAL EXCEPTION: main
Process: com.dainghia.brewfootball, PID: 21288
java.lang.ClassCastException: com.dainghia.brewfootball.ui.UiState$Loading cannot be cast to com.dainghia.brewfootball.ui.UiState$Success
at com.dainghia.brewfootball.ui.main.composable.FixtureListKt$FixtureList$2$1.invoke(FixtureList.kt:53)
at com.dainghia.brewfootball.ui.main.composable.FixtureListKt$FixtureList$2$1.invoke(FixtureList.kt:52)
at androidx.compose.foundation.lazy.LazyListItemProviderKt$rememberLazyListItemProvider$1$itemProviderState$1.invoke(LazyListItemProvider.kt:54)
at androidx.compose.foundation.lazy.LazyListItemProviderKt$rememberLazyListItemProvider$1$itemProviderState$1.invoke(LazyListItemProvider.kt:53)
at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2200)
at androidx.compose.runtime.DerivedSnapshotState.currentRecord(DerivedState.kt:161)
at androidx.compose.runtime.DerivedSnapshotState.getCurrentValue(DerivedState.kt:231)
at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.recordInvalidation(SnapshotStateObserver.kt:523)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.drainChanges(SnapshotStateObserver.kt:66)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.access$drainChanges(SnapshotStateObserver.kt:38)
at androidx.compose.runtime.snapshots.SnapshotStateObserver$applyObserver$1.invoke(SnapshotStateObserver.kt:45)
at androidx.compose.runtime.snapshots.SnapshotStateObserver$applyObserver$1.invoke(SnapshotStateObserver.kt:43)
at androidx.compose.runtime.snapshots.SnapshotKt.advanceGlobalSnapshot(Snapshot.kt:1768)
at androidx.compose.runtime.snapshots.SnapshotKt.advanceGlobalSnapshot(Snapshot.kt:1779)
at androidx.compose.runtime.snapshots.SnapshotKt.access$advanceGlobalSnapshot(Snapshot.kt:1)
at androidx.compose.runtime.snapshots.Snapshot$Companion.sendApplyNotifications(Snapshot.kt:568)
at androidx.compose.ui.platform.GlobalSnapshotManager$ensureStarted$1.invokeSuspend(GlobalSnapshotManager.android.kt:46)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch(AndroidUiDispatcher.android.kt:81)
at androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch(AndroidUiDispatcher.android.kt:41)
at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.run(AndroidUiDispatcher.android.kt:57)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7884)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.ui.platform.AndroidUiFrameClock@b9756e1, StandaloneCoroutine{Cancelling}@eeebc06, AndroidUiDispatcher@b9412c7]
I tried to make this change in Composable
function:
val uiState = fixtureStateFlow.collectAsState().value
And this works.
Could you please help me understand the root cause of this issue?
>` return true but casting to (2) `uiState as UiState.Success
– dainghia.tran Jun 05 '23 at 06:37>` throws an execption. In the stack trace `com.dainghia.brewfootball.ui.UiState$Loading cannot be cast to com.dainghia.brewfootball.ui.UiState$Success` looks like the value at (1) is the new value and when (2) is executing it receives the old value