Reputation: 2737
My project uses SafeArgs. I've switched to a branch that someone else created, and building the project generates a compiler error, because the generated "~Directions" class' methods for return ActionOnlyNavDirections (no arguments passed to destination fragment) even though in the nav_graph, the fragment takes an argument.
For example, with the following nav_graph.xml:
<fragment
android:id="@+id/fragment_a"
android:name="com.myapp.ui.fragment.FragmentA"
android:label="Fragment A"
tools:layout="@layout/fragment_a">
<argument
android:name="userName"
android:defaultValue=" "
app:argType="string" />
<action
android:id="@+id/action_fragment_a_to_fragmentX"
app:destination="@id/fragmentX" />
<action
android:id="@+id/action_fragment_a_to_homeFragment"
app:destination="@id/homeFragment" />
<action
android:id="@+id/action_fragmentA_to_fragmentC"
app:destination="@id/fragmentC"
app:popUpTo="@id/loginFragment" />
</fragment>
<fragment
android:id="@+id/fragment_b"
android:name="com.myapp.ui.fragment.FragmentB"
android:label="Fragment B"
tools:layout="@layout/fragment_b">
<argument
android:name="from"
app:argType="com.myapp.data.local.model.ToFragmentBFrom"/>
<action
android:id="@+id/action_fragmentB_to_homeFragment"
app:destination="@id/homeFragment" />
<action
android:id="@+id/action_fragmentB_to_fragmentC"
app:destination="@id/fragmentC"
app:popUpTo="@id/homeFragment" />
</fragment>
<fragment
android:id="@+id/fragment_c"
android:name="com.myapp.fragment.fragmentC"
android:label="Fragment C"
tools:layout="@layout/fragment_c">
<argument
android:name="userName"
app:argType="string" />
</fragment>
I wind up with the following Directions classes:
class FragmentADirections private constructor() {
private data class ActionFragmentAToFragmentC(val userName: String) : NavDirections {
override fun getActionId(): Int = R.id.action_fragmentA_to_fragmentC
override fun getArguments(): Bundle {
val result = Bundle()
result.putString("userName", this.userName)
return result
}
}
companion object {
fun actionFragmentAToFragmentX(): NavDirections =
ActionOnlyNavDirections(R.id.action_fragmentA_to_fragmentX)
fun actionFragmentAToHomeFragment(): NavDirections =
ActionOnlyNavDirections(R.id.action_fragmentA_to_homeFragment)
fun actionFragmentAToFragmentC(userName: String): NavDirections =
ActionFragmentAToFragmentC(userName)
fun actionGlobalFragmentA(userName: String = " "): NavDirections =
NavGraphDirections.actionGlobalFragmentA(userName)
}
}
and:
class FragmentBDirections private constructor() {
companion object {
fun actionFragmentBToHomeFragment(): NavDirections =
ActionOnlyNavDirections(R.id.action_fragmentA_to_homeFragment)
fun actionFragmentBToFragmentC(): NavDirections =
ActionOnlyNavDirections(R.id.action_fragmentB_to_fragmentC)
}
}
AS you can see, FragmentC takes a "userName" argument, and the actionFragmentAToFragmentC respects this, whereas actionFragmentBToFragmentC does not. I've tried cleaning, manually deleting the build folder, invalidating cache and restarting, and rebuilding but still the generated classes always look the same. Why is SafeArgs generating arguments for one Directions class and not the other?
How can I debug the SafeArgs plugin at build time to learn more about what's causing this?
Upvotes: 12
Views: 2621
Reputation: 24
Default value is defined in FragmentA as
android:defaultValue=" "
but not in FragmentB. Navigation component doesn't know what to assign to non nullable String by default.
Try doing the same in fragment B.
Upvotes: 0
Reputation: 31
I changed from "safeargs" to "safeargs.kotlin" and it worked. Although the Docs said "safeargs" should work on both Java and Kotlin.
plugins {
// id 'androidx.navigation.safeargs'
id 'androidx.navigation.safeargs.kotlin'
}
Upvotes: 3
Reputation: 11
For me this was caused by having my source and destination fragment in the same xml file but under different navigation elements. Moving both fragments under the same navigation element generated the arguments
I changed from this (with a nested navigation element)
<navigation
...
android:id="@+id/nav_main">
<fragment
android:id="@+id/fragment_a"
android:name="com.myapp.ui.fragment.FragmentA"
android:label="Fragment A"
tools:layout="@layout/fragment_a">
<action
android:id="@+id/action_fragment_a_to_fragment_b"
app:destination="@id/fragment_b" />
</fragment>
<navigation
...
android:id="@+id/nav_other">
<fragment
android:id="@+id/fragment_b"
android:name="com.myapp.ui.fragment.FragmentB"
android:label="Fragment B"
tools:layout="@layout/fragment_b">
<argument
android:name="from"
app:argType="com.myapp.data.local.model.ToFragmentBFrom"/>
</fragment>
</navigation>
</navigation>
To this
<navigation
...
android:id="@+id/nav_main">
<fragment
android:id="@+id/fragment_a"
android:name="com.myapp.ui.fragment.FragmentA"
android:label="Fragment A"
tools:layout="@layout/fragment_a">
<action
android:id="@+id/action_fragment_a_to_fragment_b"
app:destination="@id/fragment_b" />
</fragment>
<fragment
android:id="@+id/fragment_b"
android:name="com.myapp.ui.fragment.FragmentB"
android:label="Fragment B"
tools:layout="@layout/fragment_b">
<argument
android:name="from"
app:argType="com.myapp.data.local.model.ToFragmentBFrom"/>
</fragment>
</navigation>
It seems as though you may need to double-define arguments (both in the destination and in the argument) when navigating across navigation elements or across entire graphs although I don't see any specific mention of this in the documentation
Upvotes: 1
Reputation: 1339
This sounds and feels a little bit awkward, since the generated FragmentBDirections.class
should contain FragmentC
's arguments automatically. From time to time I face this problem as well, but I can't tell you why the safe args plugin behaves this way and especially why it doesn't behaves always similary.
To fix this, add the <argument>
explicitly in the <action>
.
<action
android:id="@+id/action_fragmentB_to_fragmentC"
app:destination="@id/fragmentC"
app:popUpTo="@id/homeFragment" />
<argument
android:name="userName"
app:argType="String" />
</action>
For sure, this produces some code overhead in the NavGraph since you need to specify the arguments always in the <fragment>
and the <action>
. This does not seam to be the "cleanest" solution. But this works for me always and I can still profit of the SafeArgs.
Upvotes: 2