简体   繁体   中英

How to navigate from a screen to another in Jetpack Compose using navController?

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?

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 (ie auth state can change), then instead of Unit use viewModel.isUserAuthenticated as the key for LaunchedEffect (assuming that isUserAuthenticated is a State )

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

在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM