Search code examples
kotlinandroid-activityandroid-navigation

Navigation Component: How to clear back stack WHEN NAVIGATING FROM FRAGMENT TO ACTIVITY?


Please make sure to read the full question before marking it as repeated.

I am well aware of popUpto, popUpToInclusive and launchSingleTop attributes on navigation xml. They simply do NOT apply when navigating from a FRAGMENT to a ACTIVITY (even though they still appear in the navigation xml). I have tried dozens of different solutions and I am searching for an answer to this for the past 3 days!

To let you guys know, this is the ONLY solution so far that actually works: https://stackoverflow.com/a/59795636/6952763

But the solution itself is so ugly that I am not willing to implement it myself. Relaunching the same activity and accepting the flashing black screen when doing it for me just doesn't seem like an option.

The closest I have got from a good working solution was https://stackoverflow.com/a/63566415/6952763 which is based on https://stackoverflow.com/a/63169732/6952763 but for some reason when adding the combination of Intent.FLAG_ACTIVITY_CLEAR_TASK together with Intent.FLAG_ACTIVITY_NEW_TASK results on the target Activity to be created/destroyed/recreated multiple times (around 8 times). It's easy to achieve it when navigating between Fragments. Just the xml parameters that I mentioned above already do the work. The ask here is from FRAGMENT to ACTIVITY which is giving me headaches :D

Summarising the question: I want to completely clear the backstack when going from FRAGMENT_Login (example) to ACTIVITY_home using Navigation Component. Any idea how to achieve this?

So far, my code is something around this:

FRAGMENT ORIGIN

   private fun proceedToHome(user: UserEntity?){
        if(user != null && user.id != 0L){
            val direction = LoginFragmentDirections.actionLoginToMainHostActivity(user.id)
            val extras = ActivityNavigator.Extras.Builder()
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                .build()
            val navOptions = NavOptions.Builder()
                .setLaunchSingleTop(true)
                .build()
            findNavController().navigate(direction.actionId, direction.arguments, navOptions, extras)
        }
    }

ACTIVITY DESTINATION:

   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val mBinding = ActivityMainHostBinding.inflate(layoutInflater)
        setContentView(mBinding.root)
        
        navController = (supportFragmentManager.findFragmentById(R.id.activity_main_host_content) as NavHostFragment).navController
        navController.setGraph(R.navigation.main_host_navigation, intent.extras)

        mBinding.activityMainHostNavigationView.setupWithNavController(navController)
    }

The code above causes the Activity to be created/destroyed/recreated multiple times and based on my tests I think it's the combination of those 2 Intent flags I am using. If I use only Intent.FLAG_ACTIVITY_CLEAR_TASK or only Intent.FLAG_ACTIVITY_NEW_TASK then I don't get the issue but also don't clear the backstack so when user presses back button on the activity he is brought back to the fragment.

I could just call requireActivity().finish() on the origin fragment but that also seems like a hack. I feel like all I am missing is some sort of detail about the use of those flags...

Any help will be deeply appreciated! <3


Solution

  • I got it working. The above example is working just as it should, the only catch is REMOVE the POPUPTO, POPUPTOINCLUSIVE and LAUNCHSINGLETOP attributes from the ACTION related to this Fragment-Activity transition EVEN IF THESE ATTRIBUTES HAVE VALUE "FALSE". If you just toggle them off using the design of navigation xml they will still be present and toggled as false. REMOVE THEM.

    Thanks all.