M'aiq the Coder
M'aiq the Coder

Reputation: 812

Jetpack Compose: change bottom nav back press behavior with Navigation Components

I finally started using Compose for Android and I am currently stuck at navigation. I have two questions:

1. I have implemented a bottom navigation with three buttons and one of the buttons triggeres a nested navigation graph, like:

 HOME  ---  CONTACTS  ---  FAVOURITES 
   |            
DETAILS 

So when I open details screen from home and switch tabs, the behavior is correct, the state and last destination in the nested graph is memorized and when I tap again on home, it keeps the state and switches back to details. However, if I am on any of the other tabs and press back, it brings me to home but it resets the state, it doesn't bring me back into details. How can I achieve that?

Navigation Bar:

@Composable
fun BottomNavigationBar(navController: NavHostController) {
BottomNavigation {
    val backStackEntry by navController.currentBackStackEntryAsState()
    val currentRoute = backStackEntry?.destination?.route

    BottomNavigationCollection.items.forEach { navItem ->
        BottomNavigationItem(
            selected = currentRoute == navItem.route,
            onClick = {
                navController.navigate(navItem.route) {
                    popUpTo(navController.graph.findStartDestination().id) {
                        saveState = true
                    }
                    launchSingleTop = true
                    restoreState = true
                }
            },
            icon = {
                Icon(
                    imageVector = navItem.icon,
                    contentDescription = navItem.title
                )
            },
            label = {
                Text(text = navItem.title)
            },
        )
    }
}

Navigation Host:

@Composable
fun NavigationHost(navController: NavHostController) {
NavHost(
    navController = navController,
    startDestination = NavigationRoutes.Home.route,
    route = "root"
) {
    homeGraph(navController)

    composable(NavigationRoutes.Contacts.route) {
        ContactsScreen()
    }

    composable(NavigationRoutes.Favorites.route) {
        FavouritesScreen()
    }
}

}

Nested Home Navigation Graph:

fun NavGraphBuilder.homeGraph(navController: NavController) {
navigation(
    startDestination = NavigationRoutes.Dashboard.route,
    route = NavigationRoutes.Home.route
) {
    composable(NavigationRoutes.Dashboard.route) {
        DashboardScreen(navController)
    }

    composable(NavigationRoutes.Details.route) {
        DetailsScreen(navController)
    }
}

}

2. When I am back in home with the entire navigation stack clear and press back once more, naturally the app closes. How can I intercept back press and implement a "tap again to exit" confirmation feature at that point?

Upvotes: 8

Views: 4473

Answers (2)

BenjyTec
BenjyTec

Reputation: 10375

I resolved the problem using an empty BackHandler. As your Contacts and Favorites screens do not have any nested navigation themselves, this should work in your case. The BackHandler will consume the back press and will prevent it from being interpreted as popBackStack.

@Composable
fun Contacts() {
    val context = LocalContext.current
    
    BackHandler {
        // leave empty
    }
}

In the BackHandler, you also could place a "click twice to close app" behavior. You can try it like this:

@Composable
fun Contacts() {
    val context = LocalContext.current
    val coroutineScope = rememberCoroutineScope()
    var firstPressed by remember { mutableStateOf(false) }
    
    BackHandler() {
        if (firstPressed) {
            val activity = (context as? Activity)
            activity?.finish()
        } else {
            firstPressed = true
            Toast.makeText(context, "Press back again to exit", Toast.LENGTH_SHORT).show()
            coroutineScope.launch {
                delay(2000L)  // set delay here as wished
                firstPressed = false
            }   
        }
    }
}

Upvotes: 1

SimonSays
SimonSays

Reputation: 10977

I cannot answer your first question, but for your second one: I just stumbled upon BackHandler. I just tried it real quick and it got called instead of closing the app, so that should solve your issue.

Upvotes: 1

Related Questions