Search code examples
android-jetpackandroid-jetpack-composeandroid-jetpack-navigation

Scroll position lost when back button pressed in Jetpack Compose


I am using a Bottom Navigation Bar and each menu navigates the user to a specific composable screen. I've used the navigation library to manage the navigation between them.

I am using a common ViewModel for all the composables. I am using a lazy column in one of the composables and when I navigate between the menu items by clicking on them in the Bottom Navigation bar, then it works as expected where in the scroll position of the lazy column is being saved.

The problem occurs when I click the button(which I have programmed to navigate to the start destination in the navigation graph) while in the composable screen that has a lazyColumn and navigate back to it then the scroll position is refreshed to 0. Is this a bug or am I doing something wrong

Here is my code:

The Navigation

@Composable
fun PopulateHomeComposables(navController: NavHostController,             
homeViewModel: HomeViewModel, lifecycleScope : LifecycleCoroutineScope) {
NavHost(navController = navController, startDestination = 
WHATS_NEW_COMPOSABLE) {

    composable(WHATS_NEW_COMPOSABLE) {
        WhatsNewComposable(navController)
    }
    composable(COMPLIMENTS_COMPOSABLE) {
        ComplimentsComposable(navController)
    }
    composable(INSIGHTS_COMPOSABLE) {
        InsightsComposable(navController)
    }
    composable(NOTIFICATIONS_COMPOSABLE) {

        NotificationsComposable(homeViewModel, lifecycleScope)
    }
    }
}

And my Scaffold looks like this

Scaffold(
    topBar = {

    },
    content = {

    },
    floatingActionButton = {
    },
    bottomBar = {
        val items = listOf(BottomNavScreens.WhatsNew, 
BottomNavScreens.Compliments, BottomNavScreens.Insights, 
BottomNavScreens.Notifications)
        BottomNavigation {
            val navBackStackEntry by navController.currentBackStackEntryAsState()
            val currentRoute = navBackStackEntry?.destination?.route
            //draw the menu items for each one
            items.forEach {
                BottomNavigationItem(
                    icon = {
                        Icon(it.icon, it.label)
                    },
                    label = {
                        Text(it.label)
                    },
                    alwaysShowLabel = true,
                    selected = currentRoute == it.route,
                    onClick = {
                        navController.navigate(it.route) {
               // Pop up to the start destination of the graph to
               // avoid building up a large stack of destinations
                            // on the back stack as users select items
                            
navController.graph.startDestinationRoute?.let { route ->
                                popUpTo(route) {
                                    saveState = true
                                }
                            }
                     //Avoid multiple copies of the same estination when
                            //re selecting the same item
                            launchSingleTop = true
         //Restore state when re selecting a previously selected item
                            restoreState = true
                        }
                    }
                )
            }
        }
    }
)

Solution

  • So i solved this. Since I was using the navigation library, I expected it to take care of saving the composable state when back button is clicked while using the bottomNavBar. But all I had to do was override the back button press inside a composable and add a function to navigate to the start destination using the navHostController.

    //override the back button press to navigate back to the 
    //whatsNewComposable
    BackHandler {
        Timber.i("Back button clicked in notifications composable")
    
        navController.navigate("myStartDestinationRoute) {
        // Pop up to the start destination of the graph to
        // avoid building up a large stack of destinations
        // on the back stack as users select items
        navController.graph.startDestinationRoute?.let { route ->
            popUpTo(route) {
                saveState = true
            }
        }
        //Avoid multiple copies of the same destination when
        //re selecting the same item
        launchSingleTop = true
        //Restore state when re selecting a previously selected item
        restoreState = true
    }