Remove specefic fragment from backstack that is not on top of backstack

Short Version:

I have 5 fragments all added to a container by replace method(A -> B -> C -> D -> E), now I want to remove 3rdfragment from my backStack(C) and stay in the current page, how to do it?

The problem:

I have 3 fragments, FragmentA, FragmentB, and FragmentC, and user navigates from A to B then C. In FragmentC when user performs an action (I call it ActionX) I need to remove FragmentB from my fragment manager BackStack (while user still is in FragmentC) and I don't want user to notice anything, just when he presses back on phone different fragment shows (so if ActionX happened when user pressed back fragmentA shows, if not FragmentB shows)

This is how I show my fragments (I set fragment name for name and tag to find them when I need)

public void showFragment(Fragment fragment) {
        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        String tag = fragment.getClass().getSimpleName();
        fragmentTransaction.replace(R.id.fragment_container, fragment, tag);
        fragmentTransaction.addToBackStack(tag);
        fragmentTransaction.commit();
}

I tried solutions mentiond in some SO threads (like this) but they only work on top fragment in stack or they suggest to not FragmentB (which I cant because I don't know what will user do), also finding and removing FragmentB from stack does not work. One solution is to override my Activity's onBackPressed and check if ActionX happend and pop my backStack twice.

what I tried:

  1. this don't work (fragmentBInStack is not null but when I remove it nothing happens)
val fragmentBInStack = manager.findFragmentByTag(FragmentB::class.java.simpleName)
fragmentBInStack?.let {
    manager.beginTransaction().remove(it).commit()
}
  1. this code removes both FragmentC and FragmentB and shows FragmentA (I want to stay in FragmentC) if I user 0 instad of POP_BACK_STACK_INCLUSIVE it only pops FragmentC and shows FragmentB
manager.popBackStack(FragmentB::class.java.simpleName, FragmentManager.POP_BACK_STACK_INCLUSIVE)

this is some sample code of what I want to do

class FragmentC : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        view.findViewById<View>(R.id.action_x).setOnClickListener {

            //here I want to remove FragmentB from back Stack and stay in FragmentC, just later when user presses back I want to show FragmentA
            val manager = activity!!.supportFragmentManager
            val fragmentBInStack = manager.findFragmentByTag(FragmentB::class.java.simpleName)
            fragmentBInStack?.let {
                manager.beginTransaction().remove(it).commit()
            }
        }
    }

2 answers

  • answered 2020-01-18 11:52 mac229

    Ok, first of all, you have to add your fragments in seperate transaction, and change replace to add

    supportFragmentManager
        .beginTransaction()
        .add(R.id.fragment_container, fragment2, tag2)
        .addToBackStack(tag2)
        .commit()
    

    Then you remove fragment as you do:

    val fragment2 = supportFragmentManager.findFragmentByTag(tag2)!!
    supportFragmentManager.beginTransaction().remove(fragment2).commit()
    

    The last step is just to popBackStack:

    supportFragmentManager.popBackStack()
    

  • answered 2020-01-18 13:00 art

    I can suggest the following approach: use the same instance of ViewModel (use ViewModelProvider of activity to create view model instance) with ActionX event LiveData in FragmentB and FragmentC. In FragmentC the value of LiveData is set, while FragmentB observes live data changes.

    Here is the source code.

    1. view model

    class InterFragmentViewModel : ViewModel() {
        val actionXLiveData = MutableLiveData<Boolean>()
    }
    

    2. FragmentC

    class FragmentC : AppCompatDialogFragment() {
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            view.findViewById<View>(R.id.action_x).setOnClickListener {
                ViewModelProvider(requireActivity()).get(InterFragmentViewModel::class.java)
                    .actionXLiveData.value = true
            }
        }
    }
    

    3. FragmentB

    class FragmentB : AppCompatDialogFragment() {
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
    
            ViewModelProvider(requireActivity()).get(InterFragmentViewModel::class.java)
                .actionXLiveData.observe(viewLifecycleOwner, Observer {
                parentFragmentManager.popBackStack()
            })
    
        }
    }