Search code examples
android-jetpack-composeandroid-jetpackandroid-architecture-navigationandroid-navigationandroid-navigation-bar

Bottom Navigation in compose, how to navigate outside of Scoffold/NavGraph


What I use:

Kotlin, Jetpack Compose

What I want to do:

After clicking "Log In" text I would want to navigate user to the log in form

What I currently have:

MainActivity.kt

class MainActivity : ComponentActivity() {

    private val viewModel: MainActivityViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        installSplashScreen().apply {
            setKeepOnScreenCondition {
                viewModel.isLoading.value
            }
        }
        setContent {
            RestaurantioTheme {
                Surface(Modifier.fillMaxSize()) {
                    val navController = rememberNavController()
                    Scaffold(
                        content = {padding ->
                            Column(
                                modifier = Modifier.padding(padding)
                            ) {
                                Navigation(navController = navController)
                            }
                        },
                        bottomBar = {
                            BottomNavigationBar(
                                items = listOf(
                                    BottomNavItem(
                                        name = "Home",
                                        route = "home",
                                        icon = Icons.Outlined.Home
                                    ),
                                    BottomNavItem(
                                        name = "Orders",
                                        route = "orders",
                                        icon = Icons.Outlined.ShoppingBag
                                    ),
                                    BottomNavItem(
                                        name = "Map",
                                        route = "map",
                                        icon = Icons.Outlined.Map
                                    ),
                                    BottomNavItem(
                                        name = "Profile",
                                        route = "profile",
                                        icon = Icons.Outlined.Person
                                    ),


                                ),

                                navController = navController,
                                onItemClick = {
                                    navController.navigate(it.route) {
                                        popUpTo(navController.graph.findStartDestination().id) {
                                            saveState = true
                                        }
                                        launchSingleTop = true
                                        restoreState = true
                                    }
                                }
                            )

                        }
                    )

               }
            }
        }
    }
}

BottomNav.kt

@Composable
fun Navigation (navController: NavHostController) {

    NavHost(navController = navController, startDestination = "home" ) {

        composable("home") {
            HomeScreen()
        }

        composable("orders") {
            OrdersScreen()
        }

        composable("map") {
            MapScreen()
        }

        composable("profile") {
            ProfileScreen()
        }

        composable("login") {
            MapScreen()
        }
    }
}

What I've tried:

Inside my ProfileScreen() Composable I have

        ClickableText(
            AnnotatedString("Log In"),
            onClick = {

            })

But I can't navigate to login screen. I've tried something like this:

val navController = rememberNavController()
        Navigation(navController)
        ClickableText(
            AnnotatedString("Log In"),
            onClick = {
                navController.navigate("login")
            })

but it doesn't work as intended, because I have HomeScreen(start screen) on top of my ProfileScreen, and after clicking LogIn text I have this "temporary" MapScreen.

I know that I'm doing something wrong, but I've just started learning compose and I can't really understand navigation documentation for nested navigation, which as I suppose should be used here.

I've tried to make additional nav graph for Profile->login route, but I couldn't open Profile screen, because of lack of memory

Adding nested navigation as per documentation doesn't work att all in my case

If it helps, below is the beginning of ProfileScreen Composable, maybe I have to pass some argument there.

@Composable
fun ProfileScreen(viewModel: AuthenticationViewModel = androidx.lifecycle.viewmodel.compose.viewModel()) {
...
}

Image from Application


Solution

  • So after few days of struggle, I've managed to partially solve my issue with navigation, now I have to figure out how to remove bottom navigation bar and add back button or change bottom navigation behaviour after navigating to Login Screen, because after that navigation, "Profile" icon isn't selected and I can't go back to Profile Screen unless I press back button.

    What I did to resolve this issue:

    BottomNav.kt

    @Composable
    fun ProfileScreen(onItemClick: () -> Unit, // here I've added lambda function
        viewModel: AuthenticationViewModel = androidx.lifecycle.viewmodel.compose.viewModel()) {...}
    

    I've added simple button without styling etc to navigate:

    Button(onClick = onItemClick) {
                
            }
    

    And the last thing, inside of Navigation function I've edited ProfileScreen composable:

    @Composable
    fun Navigation (navController: NavHostController) {
    
        NavHost(navController = navController, startDestination = "home" ) {
    
            composable("home") {
                HomeScreen()
            }
    
            composable("orders") {
                OrdersScreen()
            }
    
            composable("map") {
                MapScreen()
            }
    
            // this was edited
            composable("profile") {
                ProfileScreen(onItemClick = {
                    navController.navigate("login")
                })
    
            }
    
            composable("login") {
                    LoginScreen()
            }
        }
    }