by navGraphViewModels creates java.lang.IllegalArgumentException: No destination with ID <destination id> is on the NavController's back stack
Asked Answered
S

5

13

When a configuration change happens and my Activity and Fragment are recreated because of it, my nav Graph scoped ViewModel is unavailable while the Fragments have already been created again.
It seems like the Fragment gets recreated, before the navGraph does.

I am using this code to initialize my navGraph scoped ViewModel from my Fragment:

private val myViewModel: MyViewModel by navGraphViewModels(R.id.nav_graph_id)

If I try to use myViewModel in the Fragments onViewCreated function, I get a IllegalArgumentException after a Configuration change. The Exception:

java.lang.IllegalArgumentException: No destination with ID <destination id> is on the NavController's back stack

How do I handle this?

I have already checked that my ID isn't used anywhere else.

Edit1:

Here is my activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
    <androidx.fragment.app.FragmentContainerView
            android:id="@+id/main_nav_host"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:defaultNavHost="true"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:navGraph="@navigation/main_nav_graph" />

</androidx.constraintlayout.widget.ConstraintLayout>

And here is my main_nav_graph.xml:

<navigation
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/main_nav_graph"
        app:startDestination="@id/main_nav_graph_1">
    
    <navigation
            android:id="@+id/main_nav_graph_1"
            android:label="@string/nav_graph_1_label"
            app:startDestination="@id/nav_graph_1_start_fragment">
        <!-- nav graph stuff -->
    </navigation>
    
    <navigation
            android:id="@+id/main_nav_graph_2"
            android:label="@string/nav_graph_2_label"
            app:startDestination="@id/nav_graph_2_start_fragment">
        
        <fragment
                android:id="@+id/nav_graph_2_start_fragment"
                android:label="@string/nav_graph_2_start_fragment_label"
                android:name="my.package.ui.NavGraph2StartFragment"
                tools:layout="@layout/fragment_nav_graph_2_start">
        </fragment>
        <!-- In here is where I get the problem -->
    </navigation>
    
</navigation>
Scagliola answered 7/9, 2020 at 15:22 Comment(7)
Are you using the app:navGraph field in XML? Or are you manually calling setGraph on your NavController? Can you include that code?Administration
I have edited my post. Is this the code that you wanted?Scagliola
What version of Navigation and Fragments are you using?Administration
@Administration We've got the same problem and unfortunately the solution by AndroidKotlinNoob doesn't work for us. We start to observe the viewmodel in the onViewCreated() method and that does seem to be too early after activity recreation. The NavHostController contains the valid entries in the mBackStackToRestore property but the mBackStack array is still empty. We have to wait until the (deprecated) onActivityCreated() callback to make it work. Using Fragment 1.3.0-beta01, Activity 1.2.0-beta01 and Navigation 2.3.1.Heptad
@Heptad I am using Navigation 2.3.1 and appcompat 1.2.0 and don't have that problem. Even if I update my appcompat library to 1.3.0-alpha02 it still works. Maybe you should make a seperate Post about this, because it will probably get more attention.Scagliola
@Scagliola How did you fix this problem?Magisterial
@Heptad thank you, moving initialization logic to onStart() fixes the problem for mePregnant
S
6

My Problem was the following:

After a Configuration change the NavGraph returned to its start destination, but the Fragment that was last active gets loaded anyway. This meant that the Fragment that was actually started and the current destination of the navGraph were out of sync.
When my Fragment then tried to load the navGraph scoped ViewModel, it failed because the navGraph thought it was on a different Fragment then it actually was.

To fix my problem I had to save and restore the state of the navController using the activities savedInstanceState Bundle. (source: https://stackoverflow.com/a/59987336)

override fun onSaveInstanceState(savedInstanceState: Bundle) {
    super.onSaveInstanceState(savedInstanceState)
    savedInstanceState.putBundle("nav_state", fragment.findNavController().saveState())
}

// restore in RestoreInstanceState
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    super.onRestoreInstanceState(savedInstanceState)
    fragment.findNavController().restoreState(savedInstanceState.getBundle("nav_state"))
}

// or restore in onCreate
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    if (savedInstanceState?.containsKey("nav_state") == true) {
        fragment.findNavController().restoreState(savedInstanceState.getBundle("nav_state"))
    }
}
Scagliola answered 8/9, 2020 at 8:20 Comment(2)
Isn't the Navigation saving/restoring supposed to be working automatically out-of-the-box?Harleyharli
Yes, it does work out-of-the-box but only when not modifying the inflated navigation graph according to this: developer.android.com/guide/navigation/use-graph/…Scagliola
N
5

For anyone else coming here, I experienced this issue when a dialog was currently displayed and I issued a navController.navigate() call to a Fragment which uses by navGraphViewModels() before the dialog was dismissed.

It's as if the Navigation library couldn't understand that there is a destination with that ID available when a dialog is being displayed.

And for more clarity, this dialog is displayed using the <dialog> attribute in my nav graph XML.

My fix (for now) is to ensure the dialog is dismissed before making a further call to navigate elsewhere by way of view.postDelayed({ navController.navigate(elsewhere)}, 300)

This isn't the first time I've experienced issues with the NavController trying to navigate when a dialog is displayed.

Nesmith answered 3/2, 2021 at 20:7 Comment(1)
For me, I was using "by navGraphViewModels()" but I was providing a wrong nav id to it (was an id of a nav_graph that isn't in the dialog nav_graph)Toxoid
R
1

My problem was that I defined the fragment with the same id in the nav graph and in an included nav graph.

Ramadan answered 14/3, 2023 at 20:24 Comment(0)
O
0

This issue would happen if you're using hiltNavGraphViewModels and trying to navigate onCreateView.

it's mentioned as:

This property can be accessed only after this NavGraph is on the NavController back stack, and an attempt access prior to that will result in an IllegalArgumentException.

So better navigate onViewCreated

Offload answered 11/12, 2023 at 11:26 Comment(0)
F
-6

Solved Easy by change this :

private val myViewModel: MyViewModel by hiltNavGraphViewModels(R.id.nav_graph_id)

To:

private val viewModel: AuthViewModel by activityViewModels{ defaultViewModelProviderFactory }
Fabianfabianism answered 7/12, 2021 at 21:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.