android / architecture-components-samples

Samples for Android Architecture Components.
https://d.android.com/arch
Apache License 2.0
23.47k stars 8.29k forks source link

Use same fragment multiple times in different navigation graph #769

Open Motazlubbad opened 5 years ago

Motazlubbad commented 5 years ago

I would like to navigate to fragment A in more than one graph.

"Now I get this error : navigation destination action_compareListFragment_to_productPageFragment is unknown to this NavController"

How should I handle this situation?

Rissmon commented 4 years ago

Having the same issue. Any solution?

DJDrama commented 4 years ago

always!! i was also facing that problem. I hope this can help you. check the current destination id like for example if (findNavController().currentDestination?.id == R.id.currentfragmentname) { findNavController().navigate(R.id.action_currentfragmentname_to_anyfragmentname) }

handhikadj commented 4 years ago

+1 I'm having this problem too with jetpack navigation v.2.1.0

@DJDrama can you share the complete snippet of it?

inromualdo commented 4 years ago

Just use nested navigation.

Put the fragment in different navigation file and add it as nested navigation in other file.

pankajjangid commented 4 years ago

You Just have set destination id same as your fragment id.

Like below

<fragment
        android:id="@+id/externalProfileFragment"
        android:name="com.social.footprint.ui.external_user_profile.ExternalProfileFragment"
        android:label="ExternalProfileFragment"
        tools:layout="@layout/external_profile_fragment">
        <argument
            android:name="user_id"
            app:argType="string" />
        <action
            android:id="@+id/action_externalProfileFragment_to_externalProfileFragment"
            app:destination="@id/externalProfileFragment" />

</fragment>
marcelo-s commented 4 years ago

I want to do the same but I think this is not possible:

The nested graph encapsulates its destinations. As with a root graph, a nested graph must have a destination identified as the start destination. Destinations outside of the nested graph, such as those on the root graph, access the nested graph only through its start destination.

Taken from: Nested Navigation Graphs

So basically you can only have an action that will navigate to the "start destination" of a nested graph. No way to access an inner fragment/destination inside a nested graph. I think the solution is NOT to use nested graphs whenever you want to "share" a fragment/destination across multiple graphs.

UPDATE: I had to split the "nested graph" that contained the fragment that I wanted to share. Meaning that the fragment is now available to be used as a destination from other graphs. I think this is the correct solution. This forces you to think about your app flow and how you design your navigation graphs.

Take a look at: Nested Graphs

The main idea is that nested graphs should aim to represent an "independent" or "self-contained" flow of your app. If you need to "share" one of your fragments in one graph with other graphs, then there may be something wrong with your design. In my case it made me realize that the nested graph that contained my shared fragment was not necessary and didn't actually represent an independent flow of the app, it was better embedded in the main flow, which was the other graph that wanted to have access to the shared fragment. The good thing about using graphs is that it forces you to consider all these flows details, which can eventually help you to build better UI/navigation experience for the user.

msahakyan commented 4 years ago

I have even tried to create a nested graph per destination (means that graphs will contain only one single fragment which allows me to avoid the problem mentioned by @marcelo-s) and include them into a different graphs. Unfortunately in that case SafeArgs plugin does not work properly and auto-generated FragmentDirections does not contain actions and arguments.

marcelo-s commented 4 years ago

I have even tried to create a nested graph per destination (means that graphs will contain only one single fragment which allows me to avoid the problem mentioned by @marcelo-s) and include them into a different graphs. Unfortunately in that case SafeArgs plugin does not work properly and auto-generated FragmentDirections does not contain actions and arguments.

Well if you have 1 to 1 relationship between graphs and fragments, then there is no point in having "multiple graphs". In that case you can put all of the Fragments in one graph, that way you will for sure have access to any fragment you want.

I also had the issue of the Fragment Directions not being generated. Make sure you can build your app and build it, meaning you shouldn't have any errors on your project, that way gradle/maven can compile and generate the Classes with the actions.

msahakyan commented 4 years ago

@marcelo-s It's not about 1 to 1 relationship between graphs. The idea was to create a nested subgraph (with single destination-fragment inside) for every fragment which is going to be injected into multiple graphs. Then instead of every time directly registering a corresponding destination as a <fragment id=... we can include it like <include app:graph="@navigation/subgraph" />. The advantage of this approach would be that you will have a single file (entry-point) for each shared fragment(between multiple graphs) and everywhere instead of registering the same fragment again and again (with exactly same actions and arguments) you could just include it as a subgraph. Unfortunately this approach does not work because in case of <include app:graph="@navigation/subgraph" /> when SafeArgs plugin generates FragmentDirections it does not contain actions and arguments.

alashow commented 4 years ago

Unfortunately this approach does not work because in case of when SafeArgs plugin generates FragmentDirections it does not contain actions and arguments.

Workaround: Add an action with the same id to both root graph and subgraph (ex showSome). Add arguments to SubgraphFragment, then navigate from SomeRootFragment via SubgraphFragmentDirections.showSome(args) instead of SomeRootFragmentDirections.showSome().

AbubakirKhakimov commented 8 months ago

You can create a CustomNavDirections data class and create your own NavDirections

data class CustomNavDirections( override val actionId: Int, override val arguments: Bundle ) : NavDirections

And then you can create an extension function with the argument you need and bundle can be obtained via the safe arguments

fun HomeFragmentDirections.Companion.actionHomeFragmentToTaskDetailNavGraph( taskId: Int ): NavDirections = CustomNavDirections( actionId = R.id.action_homeFragment_to_task_detail_nav_graph, arguments = TaskDetailFragmentArgs(taskId = taskId).toBundle() )

Use: findNavController().navigate( directions = HomeFragmentDirections.actionHomeFragmentToTaskDetailNavGraph( taskId = taskId ) )

redha96 commented 5 months ago

I think using global navigation action will solve this issue you can use it like below

<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/navigation"
    app:startDestination="@id/homeFragment">

<fragment
        android:id="@+id/fragment1"
        android:name="fragment1"
        android:label="fragment 1">

        <argument
            android:name="id"
            app:argType="integer" />

    </fragment>

    <action android:id="@+id/action_global_navigate_to_fragment1" app:destination="@id/fragment1"/>

</navigation>

and in you Kotlin class you can find the global action that you added in all you fragments with this navigation