Reputation: 3016
I have this structure:
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "auth"
) {
composable(
route = "auth"
) {
AuthScreen(
navController = navController
)
}
composable(
route = "profile"
) {
ProfileScreen(
navController = navController
)
}
}
When I first time open the app, I display a screen according to the authentication state:
if (!viewModel.isUserAuthenticated) {
AuthScreen(navController = navController)
} else {
ProfileScreen(navController = navController)
}
Which works fine. The problem comes, when I try to sing-in in the AuthScreen:
when(val response = authViewModel.signInState.value) {
is Response.Loading -> CircularProgressIndicator()
is Response.Success -> {
if (response.data) {
navController.navigate("profile")
Log.d(TAG, "Success")
}
}
is Response.Error -> Log.d(TAG, response.message)
}
The log statement prints "Success" but it doesn't navigate to the next ProfileScreen. How to solve this?
Upvotes: 3
Views: 6067
Reputation: 42854
Here is a much more detailed answer with code and demo if someone is looking
Code:
ComposeNavigationActivity.kt
class ComposeNavigationActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {Navigation()}
}
}
Composables
@Composable
fun MainScreen(navController: NavController) {
var inputFieldText by remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.Red),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
TextField(
value = inputFieldText,
onValueChange = {
inputFieldText = it
},
modifier = Modifier.fillMaxWidth().padding(30.dp)
)
Spacer(modifier = Modifier.height(20.dp))
Button(
modifier = Modifier.padding(5.dp),
onClick = {
navController.navigate(Screen.DetailScreen.withArgs(inputFieldText))
}) {
Text(
text = "Navigate",
color = Color.White,
textAlign = TextAlign.Center,
fontSize = 20.sp
)
}
}
}
@Composable
fun DetailScreen(name: String?) {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.Green),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
name?.let {
Text(
text = it,
color = Color.White,
textAlign = TextAlign.Center,
fontSize = 30.sp
)
}
}
}
A Sealed class to track routes
sealed class Screen(val route:String){
object MainScreen : Screen(route = "main_screen")
object DetailScreen : Screen(route = "detail_screen")
fun withArgs(vararg args:String) : String {
return buildString {
append(route)
args.forEach { arg ->
append("/$arg")
}
}
}
}
Navigation
@Composable
fun Navigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Screen.MainScreen.route) {
composable(route = Screen.MainScreen.route) {
MainScreen(navController = navController)
}
composable(
route = Screen.DetailScreen.route + "/{name}",
arguments = listOf(
navArgument("name") {
type = NavType.StringType
defaultValue = "Some Default"
nullable = true
}
)
) { entry ->
DetailScreen(name = entry.arguments?.getString("name"))
}
}
}
Output
Upvotes: 1
Reputation: 10523
You can remove that if-else from the setContent
. Instead, make ProfileScreen
as the home destination and inside it you can check whether user is authenticated or not. If he is not, navigate to the AuthScreen
@Composable
fun ProfileScreen(navController: NavController) {
LaunchedEffect(Unit) {
if(!viewModel.isUserAuthenticated) {
navController.navigate("auth")
}
}
}
If user can logout from this screen (i.e. auth state can change), then instead of Unit
use viewModel.isUserAuthenticated
as the key for LaunchedEffect
(assuming that isUserAuthenticated
is a State
)
Upvotes: 4