Issue
In navigation component, While sending arguments from first fragment to second fragment, default values are not getting which set from navigation graph.
Here is my code:
navigation_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<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_graph"
app:startDestination="@id/firstFragment">
<fragment
android:id="@+id/firstFragment"
android:name="com.example.navigationcomponent.FirstFragment"
android:label="fragment_first"
tools:layout="@layout/fragment_first" >
<action
android:id="@+id/action_firstFragment_to_secondFragment"
app:destination="@id/secondFragment"
app:enterAnim="@anim/nav_default_enter_anim" />
<argument
android:name="clickFrom"
app:argType="string"
android:defaultValue="From First Fragment" />
<argument
android:name="clickFragmentPosition"
app:argType="integer"
android:defaultValue="1" />
</fragment>
<fragment
android:id="@+id/secondFragment"
android:name="com.example.navigationcomponent.SecondFragment"
android:label="fragment_second"
tools:layout="@layout/fragment_second" />
</navigation>
FirstFragment:
class FirstFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_first, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val bundle = Bundle()
bundle.putBoolean("IsFirstFragment", true)
val navController = Navigation.findNavController(activity!!, R.id.my_nav_host_fragment)
btnNext.setOnClickListener {
navController.navigate(R.id.action_firstFragment_to_secondFragment, bundle)
}
}
}
SecondFragment:
class SecondFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_second, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val isFromFirstFragment = arguments?.getBoolean("IsFirstFragment", false)
Log.d(TAG, "$isFromFirstFragment")
Log.d(TAG, "${FirstFragmentArgs.fromBundle(arguments!!).clickFrom} ${FirstFragmentArgs.fromBundle(arguments!!).clickFragmentPosition}")
val navController = Navigation.findNavController(activity!!, R.id.my_nav_host_fragment)
btnBack.setOnClickListener {
navController.navigateUp()
}
navController.addOnDestinationChangedListener { controller, destination, arguments ->
Log.d("TAG", "${destination.label}");
}
}
companion object {
private const val TAG: String = "SecondFragment"
}
}
Here while fetching default values in second fragment I am getting Null Pointer Exception
Log.d(TAG, "${FirstFragmentArgs.fromBundle(arguments!!).clickFrom} ${FirstFragmentArgs.fromBundle(arguments!!).clickFragmentPosition}")
My Question is, How can I get values of arguments set using navigation_graph.xml
? Navigation Graph have auto-generated getter when you re-build the project. Is there any architecture to bind auto generated setters using default value?
Solution
If you want to send arguments from FirstFragment
to SecondFragment
, then you should replace your navigation_graph.xml with:
<?xml version="1.0" encoding="utf-8"?>
<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_graph"
app:startDestination="@id/firstFragment">
<fragment
android:id="@+id/firstFragment"
android:name="com.example.navigationcomponent.FirstFragment"
android:label="fragment_first"
tools:layout="@layout/fragment_first" >
<action
android:id="@+id/action_firstFragment_to_secondFragment"
app:destination="@id/secondFragment"
app:enterAnim="@anim/nav_default_enter_anim" />
</fragment>
<fragment
android:id="@+id/secondFragment"
android:name="com.example.navigationcomponent.SecondFragment"
android:label="fragment_second"
tools:layout="@layout/fragment_second">
<argument
android:name="clickFrom"
app:argType="string"
android:defaultValue="From First Fragment" />
<argument
android:name="clickFragmentPosition"
app:argType="integer"
android:defaultValue="1" />
<argument
android:name="isFirstFragment"
app:argType="boolean"
android:defaultValue="false" />
</fragment>
</navigation>
Then, you can pass arguments from your FirstFragment like this:
class FirstFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_first, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val navController = Navigation.findNavController(activity!!, R.id.my_nav_host_fragment)
btnNext.setOnClickListener {
navController.navigate(
FirstFragmentDirections.actionFirstFragmentToSecondFragment( // this is an auto-generated class & method
// specify your arguments here: For example:
isFirstFragment = true,
clickFrom = "your argument here",
clickFragmentPosition = 1
// for default values, you can leave this blank
)
)
}
}
}
Then to retrieve arguments in SecondFragment
class SecondFragment : Fragment() {
private val arguments: SecondFragmentArgs by navArgs() // add this line to retrieve arguments from navigation
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_second, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val isFromFirstFragment = arguments.isFirstFragment
Log.d(TAG, "$isFromFirstFragment")
Log.d(TAG, "${arguments.clickFrom} ${arguments.clickFragmentPosition}")
val navController = Navigation.findNavController(activity!!, R.id.my_nav_host_fragment)
btnBack.setOnClickListener {
navController.navigateUp()
}
navController.addOnDestinationChangedListener { controller, destination, arguments ->
Log.d("TAG", "${destination.label}");
}
}
companion object {
private const val TAG: String = "SecondFragment"
}
}
Answered By - Christilyn Arjona
Answer Checked By - Gilberto Lyons (JavaFixing Admin)