I have these screens:
NavHost(
navController = navController,
startDestination = AuthScreen.route
) {
composable(
route = AuthScreen.route
) {
AuthScreen()
}
composable(
route = MainScreen.route
) {
MainScreen()
}
}
In the MainActivity, I need to send the user to the right screen according to the auth state. So I use:
val isUserSignedOut = viewModel.getAuthState().collectAsState().value
if (isUserSignedOut) {
navController.navigate(AuthScreen.route) {
popUpTo(navController.graph.id) {
inclusive = true
}
}
} else {
navController.navigate(MainScreen.route) {
popUpTo(navController.graph.id) {
inclusive = true
}
}
}
And everything works as expected, because I think that this is a synchronous operation. Now, when the user is authenticated, I need to create a request to check the type of user. Here is what I have tried:
if (isUserSignedOut) {
navController.navigate(AuthScreen.route) {
popUpTo(navController.graph.id) {
inclusive = true
}
}
} else {
Request(
checkAdmin = { admin ->
if (admin) {
navController.navigate(AdminScreen.route) {
popUpTo(navController.graph.id) {
inclusive = true
}
}
} else {
navController.navigate(MainScreen.route) {
popUpTo(navController.graph.id) {
inclusive = true
}
}
}
}
)
}
The problem with this code is that when I open the app, first time I get the AuthScreen displayed, even if the user is authenticated. In other words, while I get the response of the asynchronous request, the AuthScreen is displayed and I don't want that. I need go to the MainScreen or AdminScreen without displaying the AuthScreen first. How to solve this?
Edit:
@BenjyTec I'm getting the auth state from the ViewModel:
fun getAuthState() = authRepo.getAuthState(viewModelScope)
And here is the repo:
override fun getAuthState(viewModelScope: CoroutineScope) = callbackFlow {
val listener = FirebaseAuth.AuthStateListener { auth ->
trySend(auth.currentUser == null)
}
auth.addAuthStateListener(listener)
awaitClose {
auth.removeAuthStateListener(listener)
}
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),
initialValue = auth.currentUser == null
)
You are declaring the initial value of your Flow like this:
So before the async callback completes, your
isUserSignedOutwill already betrue. As a result, theAuthScreenwill be shown immediately.You will have to introduce a third state:
While the App performs the asynchronous request, initially set the state to "loading" and display a loading Composable then.
You can create an enumeration to represent the different states:
Then declare your Flow like this:
And in your Composable:
If you want the logic happen at the very start of the App, you can also set the loading Composable as
startDestinationof the NavHost and then navigate when the state becomesLOGGEDINorNOTLOGGEDIN.You can also write above logic as a
whenclause: