1

I have a single activity app that uses navigation graph and a navigation drawer to go to some of the fragments. Pressing back from each of the fragments usually brings me back to the main fragment, UNLESS I turn the screen off and back on or I put the app in the background. When I resume the app, the up button widget turns back into a hamburger menu, but navigation doesn't happen. Pressing the android back button doesn't navigate either, as if the app forgets where to navigate to.

val navController = (supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment).navController
setSupportActionBar(layoutAppBarMain.layoutToolbarMain)
NavigationUI.setupActionBarWithNavController(this@MainActivity, navController, mainDrawerLayout)
appBarConfiguration = AppBarConfiguration(navController.graph, mainDrawerLayout)
NavigationUI.setupWithNavController(mainActivityNavView, navController)
supportActionBar?.setDisplayShowTitleEnabled(false)

navController.addOnDestinationChangedListener { _: NavController, nd: NavDestination, _: Bundle? ->
    when (nd.id) {
        R.id.playFragment -> mainDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
        R.id.navRulesFragment, R.id.navImproveFragment, R.id.navAboutFragment, R.id.navDonateFragment -> mainDrawerLayout.setDrawerLockMode(
            DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
        else -> {
            binding.layoutAppBarMain.layoutToolbarMain.navigationIcon = null
            mainDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
        }
    }
}

Then overriding the onSupportNavigateUp():

override fun onSupportNavigateUp(): Boolean {
    return NavigationUI.navigateUp(navController, appBarConfiguration) || super.onSupportNavigateUp()
}
Victor Cocuz
  • 41
  • 1
  • 3

2 Answers2

2

If you don't have below code just add and try again:

    override fun onSupportNavigateUp(): Boolean {
        return NavigationUI.navigateUp(navController, null) || super.onSupportNavigateUp()
    }

Edit: If that doesn't work then try this:

layoutAppBarMain.layoutToolbarMain.setNavigationOnClickListener { onBackPressed() }
Mert
  • 904
  • 4
  • 9
  • 21
0

I found out what was causing it. I was using postponeEnterTransition() to check if the database was empty. If it is, then it should load the first fragment, else the second fragment. The problem was that I was using startPostponedEnterTransition() after the navigation had already left the first fragment, which was causing the navController to misbehave.

To solve this, I am now starting the enter transition in the first fragment, then hiding it, before navigating to the second fragment. I am also using a splash screen which is being turned off after the navigation is complete.

In MainActivity:

val splashScreen = installSplashScreen() 
splashScreen.setKeepOnScreenCondition { mainVm.keepSplashScreen.value }
super.onCreate(savedInstanceState) 

In onCreateView of the first fragment:

postponeEnterTransition() // Wait for data to load before displaying fragment
mainVm.matchState.observe(viewLifecycleOwner, EventObserver { matchState ->
            when (matchState) {
                RULES_IDLE -> mainVm.transitionToFragment(this, 0)
                // Here, if database is not empty, 
                // start transition right away and hide the view only then navigate
                // If the transition happens after the navigation has started, 
                // the navController will have issues returning from the navDrawer fragments.
                GAME_IN_PROGRESS -> {
                    startPostponedEnterTransition()
                    view?.visibility = GONE
                    navigate(RulesFragmentDirections.gameFrag()
                }
                else -> Timber.e("No implementation for state $matchState at this point")
            }
        })

Within the MainViewModel:

private val _keepSplashScreen = MutableStateFlow(true)
val keepSplashScreen = _keepSplashScreen.asStateFlow()
fun transitionToFragment(fragment: Fragment, ms: Long) = viewModelScope.launch {                     
            fragment.startPostponedEnterTransition()
            delay(ms) // Add a delay for the content to fully load before turning off the splash screen
            _keepSplashScreen.value = false
}

Aside from this, the navController implementation works correctly as per the post above.

Victor Cocuz
  • 41
  • 1
  • 3