How to reuse a fragment in different navigation graphs with safe args enabled?
R

3

15

I'm trying to reuse a fragment in different navigation graphs with safe args enabled. I noticed that if the actions are different I get a compilation error. This is because the xxxFragmentDirections autogenerated code will only generate one of the actions.

In nav_graph_1.xml:

<navigation 
  ...
  <fragment
    android:id="@+id/myFragment"
    android:name="com.example.android.MyFragment">
    <action
      android:id="@+id/next_action"
      app:destination="@+id/dest_one" />
  </fragment>
  ...

In nav_graph_2.xml:

<navigation 
  ...
  <fragment
    android:id="@+id/myFragment"
    android:name="com.example.android.MyFragment">
    <action
      android:id="@+id/other_action"
      app:destination="@+id/other_dest" />
  </fragment>
  ...

A simple use case: A bank app that has two flows: withdraw and deposit, therefore you could have two nav graphs. You could have an AmountFragment where you could just enter a number and this could be reused to either withdraw or deposit. However, depending on the flow, the actions/destinations could be different.

Then, how would it be possible to reuse this fragment?

Rustin answered 12/1, 2020 at 14:29 Comment(4)
Why, inheritance of course. Is there a better way? Probably not with safeargs.Carilla
Do you mean having a BaseAmountFragment, WithdrawAmountFragment and a DepositAmountFragment?Rustin
Yes, although I also feel that design-wise, it's a terrible idea long-term.Carilla
You can take a look at the auto-generated code and create a class on your own. Like class CustomDirections private constructor() { private data class ActionFromFragmentToFragment( val arg: Arg? = null ) : NavDirections { override fun getActionId(): Int = R.id.action_id override fun getArguments(): Bundle {…} } companion object { fun actionFromFragmentToFragment(arg: Arg? = null): NavDirections = ActionFromFragmentToFragment(arg) } }Centrifugal
R
10

Use navigate() with bundle instead of actions in edge cases. Don’t call

findNavController().navigate(FragmentDirections.goToDetailFragment(id))

but instead use

findNavController().navigate(R.id.DetailFragment, bundleOf("id" to 5))

This way you don’t rely on the generated direction but can still use the Navigation and SafeArgs features of the DetailFragment.

https://code.allaboutapps.at/articles/android-jetpack-navigation-pitfalls/#reuse-fragments-in-multiple-navigation-graphs

Rimrock answered 14/4, 2020 at 8:50 Comment(2)
Great answer. I was learning about nav components. And in the sample project, I was reusing a fragment for different input based on enums in the argument bundle. When using SafeArgs for a nested flow, I kept getting unresolved references on some actions. Tried your approach without safeArgs, problem solved, thanks.Texas
But same fragment multiple time added how to remove it?Ashkhabad
D
3

This can be achieved by providing the same action ID in both the navigation graph.

In nav_graph_1.xml:

<navigation 
  ...
  <fragment
    android:id="@+id/myFragment"
    android:name="com.example.android.MyFragment">
    <action
      android:id="@+id/next_action"
      app:destination="@+id/dest_one" />
  </fragment>
  ...

In nav_graph_2.xml:

<navigation 
  ...
  <fragment
    android:id="@+id/myFragment"
    android:name="com.example.android.MyFragment">
    <action
      android:id="@+id/next_action"
      app:destination="@+id/other_dest" />
  </fragment>
  ...

In fragment, navigation can be performed like

NavHostFragment.findNavController(<myFragment>).navigate(R.id.next);

Drool answered 8/12, 2020 at 9:50 Comment(1)
Thanks, but I'm looking for a way to also have different actions (not only destinations).Rustin
E
-1

It might not be what you ask, but I faced the problem o re-usability of code in a fragment that I needed in different navigation graphs.

The fragment class that needs to be reused should be declared as abstract:

abstract class ReusableFragment(val type: ReusableEnum): Fragment(){
    enum class ReusableEnum{Type1, Type2}
    // the rest of your logic will use 
    // 'type' variable in when() blocks to determine 
    // specific logic for each case
}

class Type1Fragment: ReusableFragment(ReusableEnum.Type1)
class Type2Fragment: ReusableFragment(ReusableEnum.Type2)

This way, Type1Fragment and Type2Fragment are available in NavigationGraph as independent fragments.

Ebersole answered 17/4, 2021 at 6:19 Comment(1)
This is just workaround for a broken api.Maldives

© 2022 - 2024 — McMap. All rights reserved.