Android navigation component: how save fragment state

30,835

Solution 1

As per the open issue, Navigation does not directly support multiple back stacks - i.e., saving the state of stack B when you go back to B from A or C since Fragments do not support multiple back stacks.

As per this comment:

The NavigationAdvancedSample is now available at https://github.com/googlesamples/android-architecture-components/tree/master/NavigationAdvancedSample

This sample uses multiple NavHostFragments, one for each bottom navigation tab, to work around the current limitations of the Fragment API in supporting multiple back stacks.

We'll be proceeding with the Fragment API to support multiple back stacks and the Navigation API to plug into it once created, which will remove the need for anything like the NavigationExtensions.kt file. We'll continue to use this issue to track that work.

Therefore you can use the NavigationAdvancedSample approach in your app right now and star the issue so that you get updates for when the underlying issue is resolved and direct support is added to Navigation.

Solution 2

In case you can deal with destroying fragment, but want to save ViewModel, you can scope it into the Navigation Graph:

private val viewModel: FavouritesViewModel by 
    navGraphViewModels(R.id.mobile_navigation) {
        viewModelFactory
    }

Read more here

EDIT

As @SpiralDev noted, using Hilt simplifies a bit:

private val viewModel: MainViewModel by 
    navGraphViewModels(R.id.mobile_navigation) {
         defaultViewModelProviderFactory     
    }

Solution 3

just use navigation component version 2.4.0-alpha01 or above

Solution 4

Update: Using last version of fragment navigation component, handle fragment states itself. see this sample

Old:

class BaseViewModel : ViewModel() {

    val bundleFromFragment = MutableLiveData<Bundle>()
}


class HomeViewModel : BaseViewModel () {

   ... HomeViewModel logic
}

inside home fragment (tab of bottom navigation)

private var viewModel: HomeViewModel by viewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    viewModel.bundleFromFragment.observe(viewLifecycleOwner, Observer {
      
        val message = it.getString("ARGUMENT_MESSAGE", "")
       binding.edtName.text = message
    })
}

override fun onDestroyView() {
    super.onDestroyView()
    viewModel.bundleFromFragment.value = bundleOf(
        "ARGUMENT_MESSAGE" to binding.edtName.text.toString(),
        "SCROLL_POSITION" to binding.scrollable.scrollY
    )
}

You can do this pattern for all fragments inside bottom navigation

Share:
30,835

Related videos on Youtube

Nikitc
Author by

Nikitc

Updated on December 08, 2021

Comments

  • Nikitc
    Nikitc over 2 years

    I use bottomNavigationView and navigation component. Please tell me how I don't destroy the fragment after switching to another tab and return to the old one? For example I have three tabs - A, B, C. My start tab is A. After I navigate to B, then return A. When I return to tab A, I do not want it to be re-created. How do it? Thanks

  • ianhanniballake
    ianhanniballake almost 5 years
    The OP said they were using the Navigation Component. You don't directly do FragmentTransactions when you use Navigation.
  • Atirek Pothiwala
    Atirek Pothiwala almost 5 years
    Yes I understand but OP wants to switch tabs only. I guess this must be a better approach.
  • Radu
    Radu over 4 years
    So a simple feature like keeping 3 screens in memory required a new 'advanced' sample? You have got to be kidding
  • ianhanniballake
    ianhanniballake over 4 years
    @Radu - it isn't just the three Fragments, it is a whole back stack associated with each tab (and the state of every one of those Fragments). FragmentManager only stores the state of things directly on the back stack (i.e., you can hit the system back button to get back to them), which isn't the case for bottom nav, where you want users to be able to swap between tabs without losing state.
  • Amin Keshavarzian
    Amin Keshavarzian over 4 years
    After all, maybe it is not that bad of an idea! my problem was that I head to redeclare fragments in each nav graphs.
  • ianhanniballake
    ianhanniballake over 4 years
    @AminKeshavarzian - you can also use an <include> tag to reference a separate graph instead of copy/pasting.
  • Amin Keshavarzian
    Amin Keshavarzian over 4 years
    @ianhanniballake you are right, but I was referring to mostly a single fragment because it wouldn't make sense to include a large graph just to add a destination from it.the trick, however, was to use identical ids.
  • Ali Khaki
    Ali Khaki over 4 years
    i use Navigation Advanced Sample and that is not save instance of fragment something is behind of this sample for saving instance
  • ianhanniballake
    ianhanniballake over 4 years
    @AliKhaki - it does indeed save and restore state correctly. You should test your Fragments in other cases, such as being put on the back stack or when rotating your device to ensure that your Fragment is written correctly to restore its state.
  • ClarkXP
    ClarkXP over 4 years
    The one solution that I works me was implement ViewModel + LiveData in each fragment where needish
  • martinseal1987
    martinseal1987 about 4 years
    they say this is coming in 2.3, were on 2.2 do i do the advance sample just to change it later? who knows
  • M.Usman
    M.Usman about 4 years
    @ianhanniballake can you please let me know, how can I use it with the navigation view?
  • sud007
    sud007 about 4 years
    @ianhanniballake Using this solution doesn't generate SafeArgs and FragmentDirection bindings. Ca you please help how can this be fixed?
  • Udayaditya Barua
    Udayaditya Barua about 4 years
    This class from the sample that @ianhanniballake is talking about, does not work. All it does it maintain the back stack and the state when you press back, that's it. Clicking on the bottom nav item, re-creates the fragment again. I fail to understand 250 lines of code for I don't know what.
  • ianhanniballake
    ianhanniballake about 4 years
    @Uday - the setupItemReselected() method is where you'd want to customize the reselection behavior. You could just do nothing if that's what is appropriate for your app.
  • binjiezhao
    binjiezhao almost 4 years
    @ianhanniballake Hello sir, any updates on this matter or folk are still confined to the advanced nav sample approach? Many thanks.
  • Zeeshan Shabbir
    Zeeshan Shabbir almost 4 years
    I almost gave up on navigation architecture components. Google should also mention the limitation of their new APIs that will make life of developer easier to decide which API is more feasible to use in longer run.
  • Amit Jayant
    Amit Jayant over 3 years
    Why all the samples are now in Kotlin?. Its highly unreadable.
  • SpiralDev
    SpiralDev over 3 years
    For DaggerHilt: private val viewModel: MainViewModel by navGraphViewModels(R.id.my_nav) { defaultViewModelProviderFactory }
  • Дмитро Яковлєв
    Дмитро Яковлєв about 3 years
    @ianhanniballake Hi Ian, does this solution still the most actual in context of 'navigation component'?
  • M Umer
    M Umer almost 3 years
    Works fine, But it clears all the variables.
  • Herlian Zhang
    Herlian Zhang almost 3 years
    yeah you need to handle it on viewmodel as usual, and if you using scrollview dont forget to add id.
  • M Umer
    M Umer over 2 years
    Yup, it is working fine with saving UI state.
  • Abbas
    Abbas over 2 years
    The latest nav library v: 2.4.0-alpha01 does support multiple back stacks out of the box Multiple Back Stack.
  • Mohmmad Qunibi
    Mohmmad Qunibi about 2 years
    It took me days trying to save the state of the fragment until I saw your answer, thank you