Fragment no longer exists for key FragmentStateAdapter with Viewpager2
D

6

22

I am using ViewPager2 for my project. I need to use nested fragments inside a fragment with viewpager2. it works like charm until I try to navigate between fragments(not nested ones).

After the first time navigating between fragments, the application crash with the error explained below.

the fragment which contains nested fragments OnCreateView method:

View view = inflater.inflate(R.layout.orders_fragment, null);

ViewPager2 viewPager = view.findViewById(R.id.childViewPager);

TabLayout tabs = view.findViewById(R.id.tabs);

SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(getChildFragmentManager(),getLifecycle());

viewPager.setAdapter(sectionsPagerAdapter);

TabLayoutMediator.TabConfigurationStrategy tabConfigurationStrategy = (tab, position) -> {

    String[] order_activity_tabs = getResources().getStringArray(R.array.situations);

    for (int i=0; i<order_activity_tabs.length; i++) {

        if(i==position)
            tab.setText(order_activity_tabs[i]);

    }
};

TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabs, viewPager, tabConfigurationStrategy);
tabLayoutMediator.attach();

return view;

When I return the fragment that contains nested fragments it crashes with

 java.lang.IllegalStateException: Fragment no longer exists for key f#0: unique id 4fbe17b8-5e22-4e07-a543-4a79445ad39c
        at androidx.fragment.app.FragmentManagerImpl.getFragment(FragmentManagerImpl.java:365)
        at androidx.viewpager2.adapter.FragmentStateAdapter.restoreState(FragmentStateAdapter.java:549)
        at androidx.viewpager2.widget.ViewPager2.restorePendingState(ViewPager2.java:350)
        at androidx.viewpager2.widget.ViewPager2.dispatchRestoreInstanceState(ViewPager2.java:375)
        at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:4045)
        at android.view.View.restoreHierarchyState(View.java:20253)
        at androidx.fragment.app.Fragment.restoreViewState(Fragment.java:548)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:907)
        at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
        at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
        at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)

There are solutions for FragmentStatePagerAdapter but there is no for FragmentStateAdapter. And because I can't override methods in FragmentStateAdapter, implementing this solutions are impossible.

Thank you for reading this. Any help is appreciated.

Dactyl answered 26/12, 2019 at 9:38 Comment(2)
before you access fragment first check it is present or not in fragment stack.Swinney
Thanks for the reply. But I am not sure to how to do this?Acerate
Z
26

Using viewpager2 with FragmentStateAdapter has a similar behaviour than FragmentStatePagerAdapter

Actually there are two ways of solving this problem

First one is seting setSaveEnabled() to false into our viewpager2

viewpager2.setSaveEnabled(false)

The other one is overriding restoreState at our adapter and return null

Check : https://developer.android.com/reference/androidx/viewpager2/adapter/FragmentStateAdapter

Important

Please note that this should not be the recommended way of disabling the state, this should work for some use cases but for production apps consider saving the state of the fragment or re-emiting the state to the UI from a state holder like viewmodel

Zaffer answered 20/1, 2020 at 0:43 Comment(5)
unfortunately, restoreState is declared as finalLafferty
I'm surprised about the number of up votes of this answer. 1. If you do that the app is not able to save and recreate the state of the previous fragment. 2. As @Lafferty said the restoreState is delcared as final.Hansiain
I think that its more fore debugging purposes rather than replacing the saving state of the fragmentPushover
it's not recommended to do this in the product version, no state will be saved for this view if you set it to false and it means when you come back from another screen to a fragment that has a view-pager, it will lose its previous data.Darksome
I totally agree @Darksome I think this is more of a quick work-around , but as you said it should never be used for production apps if we want to preserve the state. I think that we are able to change the state by ourselves for some use cases, because I don't see any purpose from the framework if they let us change this.Pushover
I
3

There is an issue with viewpager2 detach from recyclerview https://issuetracker.google.com/issues/154751401

As per my knowledge two fixes are there:

  1. add this line in your xml viewpager2 component.

    android:saveEnabled="false"
    

But the above one don't restore the current instance, which the fragment will recreate each time it popsBack.

  1. The recommended solution is add the below line on your fragment

    override fun onDestroyView() {
       super.onDestroyView()
       binding.pager.adapter = null
    }
    
Instrumentalism answered 21/10, 2022 at 7:4 Comment(1)
Can you explain why the recommended solution works?Hirsch
M
0

All you have to do is to set your viewpager2 in the layout file with the following property

android:saveEnabled="true"
Midis answered 8/6, 2021 at 8:39 Comment(1)
Shouldn't the value set to "false" instead?Deadhead
B
-1

try to call requireActivity() in your fragment argument inside FragmentStateAdapter

private inner class ScreenSlidePagerAdapter(fa: FavoriteFragment) : FragmentStateAdapter(fa.requireActivity())
Bartz answered 23/6, 2021 at 12:16 Comment(0)
V
-2

I just had the same issue. The cause is that when the containing fragment is recreated, it's childFragmentManager is as well. And the FragmentStateAdapter looks in the passed fragmentmanager to find fragments it restores state for.

My solution was to have it use the activity's fragmentManager instead of the fragment's, for example use the constructor that takes an activity like

class MyAdapter(f: Fragment) : FragmentStateAdapter(f.activity!!)

or when using the FragmentStateAdapter(FragmentManager, Lifecycle) constructor, make sure to pass parentFragmentManager and not childFragmentManager

Viewer answered 7/1, 2020 at 15:17 Comment(0)
D
-3

My problem solved with implementing new Navigation Component. It itself handles fragment transition. Anyone who across with this problem can change their navigation method.

Dactyl answered 26/12, 2019 at 12:57 Comment(1)
could you please explain more your solution?Darksome

© 2022 - 2024 — McMap. All rights reserved.