Search code examples
androidandroid-jetpackandroid-architecture-navigationandroid-navigationandroid-jetpack-navigation

When setting navigation graph programmatically after recreating activity, wrong fragment is shown


I am setting a navigation graph programmatically to set the start destination depending on some condition (for example, active session), but when I tested this with the "Don't keep activities" option enabled I faced the following bug.

When activity is just recreated and the app calls method NavController.setGraph, NavController forces restoring the Navigation back stack (from internal field mBackStackToRestore in onGraphCreated method) even if start destination is different than before so the user sees the wrong fragment.

Here is my MainActivity code:

class MainActivity : AppCompatActivity() {

    lateinit var navController: NavController
    lateinit var navHost: NavHostFragment

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
        log("fresh start = ${savedInstanceState == null}")
        navHost = supportFragmentManager.findFragmentById(R.id.main_nav_host) as NavHostFragment
        navController = navHost.navController
        createGraph(App.instance.getValue())
    }

    private fun createGraph(bool: Boolean) {
        Toast.makeText(this, "Is session active: $bool", Toast.LENGTH_SHORT).show()
        log("one: ${R.id.fragment_one}, two: ${R.id.fragment_two}")
        val graph =
            if (bool) {
                log("fragment one")
                navController.navInflater.inflate(R.navigation.nav_graph).also {
                    it.startDestination = R.id.fragment_one
                }
            } else {
                log("fragment two")
                navController.navInflater.inflate(R.navigation.nav_graph).also {
                    it.startDestination = R.id.fragment_two
                }
            }
        navController.setGraph(graph, null)
    }
}

App code:

class App : Application() {
    companion object {
        lateinit var instance: App
    }

    private var someValue = true
    override fun onCreate() {
        super.onCreate()
        instance = this
    }

    fun getValue(): Boolean {
        val result = someValue
        someValue = !someValue
        return result
    }
}

Fragment One and Two are just empty fragments.

How it looks like:

Repository with full code and more explanation available by link

My question: is it a Navigation library bug or I am doing something wrong? Maybe I am using a bad approach and there is a better one to achieve what I want?


Solution

  • Looks like there is some wrong behaviour in the library and my approach wasn't 100% correct too. At least, there is the better one and it works well. Because I am using the same graph and only changing the start destination, I can simply set that graph in onCreate of my activity and set some default start destination there. Then, in createGraph method, I can do the following:

        // pop backStack while it is not empty
        while (navController.currentBackStackEntry != null) {
            navController.popBackStack()
        }
        // then just navigate to desired destination with additional arguments if needed
        navController.navigate(destinationId, destinationBundle)