'Clear state for fragment when using bottom navigation

We have implemented bottom navigation as described here:

https://developer.android.com/guide/navigation/navigation-ui#bottom_navigation

https://medium.com/androiddevelopers/navigation-multiple-back-stacks-6c67ba41952f

We are using navigation version 2.4.1, which supports multiple backstacks out of the box. This saves fragment state so that in navigating from main fragment A -> B -> C -> B using the bottomnav, state of fragment B is saved and restored upon return. This is as intended and much requested behaviour.

However, for one of the fragments in our bottomnav menu, I would like the possibility to NOT save the state. This is due to some confusing behaviour when navigating using talkback. Is there a way in the navigation framework to set a flag to not save state for a single fragment? Or any other way to programmatically clear savedstate without actually doing so "manually" by resetting the UI elements in fragment onDestroy/onResume or similar?



Solution 1:[1]

What I did was just use the same androidx.navigation.ui.NavigationUI.setupWithNavController logic but change the saveState and other logic specific to my use case. You could apply this when navigating to one specific fragment.

        this.findViewById<BottomNavigationView>(R.id.bottom_navigation).apply {

        setOnItemSelectedListener { item ->
            val builder = NavOptions.Builder().setLaunchSingleTop(true)
            val destinationId = item.itemId
            item.isChecked = true

            if (
                navController.currentDestination!!.parent!!.findNode(item.itemId)
                        is ActivityNavigator.Destination
            ) {
                builder.setEnterAnim(R.anim.nav_default_enter_anim)
                    .setExitAnim(R.anim.nav_default_exit_anim)
                    .setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
                    .setPopExitAnim(R.anim.nav_default_pop_exit_anim)
            } else {
                builder.setEnterAnim(R.animator.nav_default_enter_anim)
                    .setExitAnim(R.animator.nav_default_exit_anim)
                    .setPopEnterAnim(R.animator.nav_default_pop_enter_anim)
                    .setPopExitAnim(R.animator.nav_default_pop_exit_anim)
            }

            if (item.order and Menu.CATEGORY_SECONDARY == 0) {
                builder.setPopUpTo(
                    navController.graph.findStartDestination().id,
                    inclusive = false,
                    saveState = false
                )
            }

            val options = builder.build()
            return@setOnItemSelectedListener try {
                navController.navigate(destinationId, null, options)
                // Return true only if the destination we've navigated to matches the MenuItem
                (navController.currentDestination?.id ?: false) == destinationId
            } catch (e: IllegalArgumentException) {
                false
            }
        }

        // Do nothing on reselect
        setOnItemReselectedListener {}

        val weakReference = WeakReference(this)
        navController.addOnDestinationChangedListener(
            object : NavController.OnDestinationChangedListener {
                override fun onDestinationChanged(
                    controller: NavController,
                    destination: NavDestination,
                    arguments: Bundle?
                ) {

                    // Hide BottomNavigationView from top level fragments
                    if (topLevelDestinations.any { it == destination.id }) {
                        [email protected] = View.VISIBLE
                    } else [email protected] = View.GONE

                    // Highlight item in BottomNavigationView
                    val view = weakReference.get()
                    if (view == null) {
                        navController.removeOnDestinationChangedListener(this)
                        return
                    }
                    view.menu.forEach { item ->
                        if (destination.id == item.itemId) {
                            item.isChecked = true
                        }
                    }
                }
            })
    }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 MakinTosH