StateFlow state's change is keep updating even there is no push inside composable.
@Composable
fun ScreenController(
viewModel: SyncViewModel, log: Logger,
navigateToLogin: () -> Unit,
navigateToSync: () -> Unit,
navigateToAttendance: () -> Unit,
navigateToDashBoard: () -> Unit,
) {
val state by viewModel.userCurrentState.collectAsState()
LaunchedEffect(Unit) {
viewModel.getUserState()
}
state.let {
if (!it.isDefault) {
if (!it.isUserLoggedIn) {
navigateToLogin()
} else if (!it.isDataSyncedForToday) {
navigateToSync()
} else if (!it.isAttendanceMarked) {
navigateToAttendance()
} else {
navigateToDashBoard()
}
}
}
}
This compose is keep calling navigateToLogin method, even though the method to get user's current state is called only once(viewModel.getUserState()) inside the launch effect.
Below is the code of NavHost
val navController = rememberNavController()
NavHost(navController, startDestination = FULL_SCREEN) {
composable(route = FULL_SCREEN) {
ScreenController(
syncViewModel,
log,
navigateToLogin = { navController.navigate(LOGIN_SCREEN) },
navigateToSync = { navController.navigate(DATA_SYNC) },
navigateToAttendance = { navController.navigate(ATTENDANCE_SCREEN) },
navigateToDashBoard = { navController.navigate(BASE_DASHBOARD) }
)
}
composable(route = LOGIN_SCREEN) {
LoginScreen(navController, loginViewModel, log)
}
}
Due to this, the LoginScreen composable keeps on recomposing itself due to the state value sending the change events even if they are not modified.
So, I am trying to understand why state value is keep updating and how?
I have tried to make the state as life cycle aware for the particular composable.
val lifecycleOwner = LocalLifecycleOwner.current
val lifecycleAwareLoginFlow = remember(viewModel.userCurrentState, lifecycleOwner) {
viewModel.userCurrentState.flowWithLifecycle(lifecycleOwner.lifecycle)
}
val state: UserAppState by lifecycleAwareLoginFlow.collectAsState(initial = UserAppState())
But still, the state value change is happening.
Adding more code -
class SyncViewModel(
private val syncRepository: SyncRepository,
log: Logger
) : ViewModel() {
private val mutableUserState: MutableStateFlow<UserAppState> =
MutableStateFlow(UserAppState())
val userCurrentState: StateFlow<UserAppState> =
mutableUserState
fun getUserState(): Job {
return viewModelScope.launch {
val isUserLoggedIn = syncRepository.isUserLoggedIn()
val isSyncDone = syncRepository.isRequiredAPIsSynced()
val isAttendanceMarked =
syncRepository.isAttendanceMarked()
log.d("App State $isUserLoggedIn, $isSyncDone,
$isAttendanceMarked")
mutableUserState.update {
it.copy(
isDefault = false,
isUserLoggedIn = isUserLoggedIn,
isDataSyncedForToday = isSyncDone,
isAttendanceMarked = isAttendanceMarked,
)
}
}
}
data class UserAppState(
val isDefault: Boolean = true,
val isUserLoggedIn: Boolean = true,
val isDataSyncedForToday: Boolean = true,
val isAttendanceMarked: Boolean = true,
)