How to send data with NavigationComponent in Android - android

In my application I have 2 fragment and for show these I use NavigationComponent and I want sent some data.
My navigation codes:
<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/nav_home"
app:startDestination="#id/recipesFragment">
<fragment
android:id="#+id/recipesFragment"
android:name="myapp.ui.fragments.recipe.RecipesFragment"
android:label="#string/recipes"
tools:layout="#layout/fragment_recipes" >
<action
android:id="#+id/action_recipesFragment_to_menuFragment"
app:destination="#id/menuFragment" />
<argument
android:name="isUpdatedData"
app:argType="boolean"
android:defaultValue="false" />
</fragment>
<dialog
android:id="#+id/menuFragment"
android:name="myapp.ui.fragments.recipe.menu.MenuFragment"
android:label="fragment_menu"
tools:layout="#layout/fragment_menu" >
<action
android:id="#+id/action_menuFragment_to_recipesFragment"
app:destination="#id/recipesFragment" />
</dialog>
</navigation>
I set argument with default value, but when use this action into MenuFragment not access to me for set value!
MenuFragment codes:
submitBtn.setOnClickListener {
val action = MenuFragmentDirections.actionMenuFragmentToRecipesFragment(true)
findNavController().navigate(action)
}
When write true for value of actionMenuFragmentToRecipesFragment show me below error:
Too many arguments for public open fun actionMenuFragmentToRecipesFragment(): MenuFragmentDirections.ActionMenuFragmentToRecipesFragment defined in myapp.ui.fragments.recipe.menu.MenuFragmentDirections
How can I fix it and set data with defaultValue?

Hi I hope this answer be useful for you
if you would like to use default value below code will help you:
findNavController().navigate(MenuFragmentDirections.actionMenuFragmentToRecipesFragment().setIsUpdatedData(true))
when you set default value for your argument compiler recognize code does not need setter and if you want change it you have to call setter method of argument in your navigation for custom fragment

Related

Android navigation component back Button doesn´t work [duplicate]

I use navigation components to navigate from one fragment to another. However, when the user press the back button, I want to navigate back to first fragment. But it keep showing the second fragment. This is my nav_graph:
<?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/nav_graph"
app:startDestination="#id/fragment1">
<fragment
android:id="#+id/fragment2"
android:name="com.myapp.ui.fragments.Fragment2"
android:label="fragment_2" />
<fragment
android:id="#+id/fragment1"
android:name="com.myapp.ui.fragments.Fragment1"
android:label="fragment_1">
<action
android:id="#+id/action_fragment1_to_fragment2"
app:destination="#id/fragment2"
app:enterAnim="#anim/fragment_fade_enter"
app:exitAnim="#anim/fragment_fade_exit"
app:popUpTo="#id/fragment1" />
</fragment>
</navigation>
And this is how I trigger the navigation in the code of my Fragment1-Class:
viewModel.itemSelected.observe(viewLifecycleOwner) {
navigate(it)
}
....
fun navigate(id: Long){
val bundle = Bundle()
bundle.putLong("itemid", id)
getNavController().navigate(R.id.action_fragment1_to_fragment2, bundle)
}
Edit:
Corrected startDestination in XML.
Edit2:
Added more code.
You're using a LiveData for an event. LiveData always caches the set value, so when you return to your Fragment1, you observe the LiveData again and get the same value a second time, causing you to navigate() yet again.
See this blog post for more information and alternatives.

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

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?
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
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);
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.

Pass arguments safely to nested graph with Navigation Component

I'm using Android Jetpack Navigation Component.
I have a nested nav graph with id, say R.id.nested_graph
The first Fragment of the nested graph receives one parameter.
<navigation
android:id="#+id/nested_graph"
android:label="Nested Graph"
app:startDestination="#id/firstFragment">
<fragment
android:id="#+id/firstFragment"
android:name="...."
android:label="....">
<argument
android:name="item_id"
app:argType="integer" />
</fragment>
[...]
</navigation>
How can I pass the parameter to the nested graph using safe args?
At the moment, I need to pass the argument manually in the bundle, using the API that receives the id of the nested graph directly:
val args = Bundle()
args.putInt("item_id", itemId)
navController.navigate(R.id.nested_graph, args)
I'd like to use safe args, and do something like:
val directions = OrigininFragmentDirections.nestedGraph(itemId)
navController.navigate(directions)
But when trying that, I get the following error at build time:
Too many arguments for public final fun nestedGraph(): NavDirections defined
The issue is that the nav graph preprocessing is generating the factory method to create the NavDirections object without the required parameter in the signature.
The declaration of the nested graph looks like this:
After some trial and error experimentation (I don't think it's officially documented by Google, or at least I could not find it), I've discovered that navigating to nested nav graphs passing arguments safely can be done:
You need to add the argument XML object the first fragment expects in the root of the nested fragment itself.
In my case, the fragment with id firstFragment, which is the first fragment in the nested graph receives:
<argument
android:name="item_id"
app:argType="integer" />
Hence, I need to add that argument to the nested graph:
<navigation
android:id="#+id/nested_graph"
android:label="Nested Graph"
app:startDestination="#id/firstFragment">
<argument
android:name="item_id"
app:argType="integer" />
<fragment
android:id="#+id/firstFragment"
android:name="...."
android:label="....">
<argument
android:name="item_id"
app:argType="integer" />
</fragment>
[...]
</navigation>
Now I can navigate to it with:
val directions = OrigininFragmentDirections.nestedGraph(itemId)
navController.navigate(directions)
Note that the navigation graph editor does not do it for you. This needs to be done manually in the XML code.
#GaRRaPeTa answer is almost correct, but if you navigate from main graph to nested graph by action using SafeArgs, you must also add an argument to the action:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/graph_main"
app:startDestination="#id/mainFragment">
<fragment
android:id="#+id/mainFragment"
android:name="com.example.MainFragment">
<action
android:id="#+id/toNestedGraph"
app:destination="#id/graph_nested">
<argument
android:name="arg_name"
app:argType="string" />
</action>
</fragment>
</navigation>

Android Navigation library crash after sending data back

I use Android Navigation library for fragment navigation and try pass data back from second fragment to first. Do it with the next code:
findNavController().setGraph(R.navigation.nav_graph, bundle)
findNavController().popBackStack()
It works fine, but when I try to open the same screen after it, the next error occurred:
java.lang.IllegalArgumentException: navigation destination com.leshchenko.test:id/openPurchaseDetailsAction is unknown to this NavController
Thanks.
UPDATE:
My 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/nav_graph"
app:startDestination="#id/purchasesListFragment">
<fragment
android:id="#+id/purchasesListFragment"
android:name="com.leshchenko.finance.presentation.purchases.PurchasesListFragment"
android:label="fragment_purchases_list"
tools:layout="#layout/fragment_purchases_list">
<action
android:id="#+id/openPurchaseDetailsAction"
app:destination="#+id/purchaseDetailsFragment">
<argument
android:name="purchaseId"
android:defaultValue="-1L"
app:argType="long" />
</action>
</fragment>
<fragment
android:id="#+id/purchaseDetailsFragment"
android:name="com.leshchenko.finance.presentation.purchase_details.PurchaseDetailsFragment"
android:label="fragment_purchase_details"
tools:layout="#layout/fragment_purchase_details">
<argument
android:name="purchaseId"
android:defaultValue="-1L"
app:argType="long" />
</fragment>
Details opens via:
findNavController().navigate(PurchasesListFragmentDirections.openPurchaseDetailsAction())
use findNavController().navigateUp() instead of findNavController().popBackStack()
I think you can just pass your data through viewModel and for fragment finish
just use :
findNavController().popBackStack()
eg:
view.setOnClickListener {
// pass Data through ViewModel
// viewModel.sendData(data)
Navigation.findNavController(it).popBackStack()
}

Could not navigate to fragment using jetpack navigation?

I want to navigate from HomeFragment to DetailFragment
in RecyclerView item click I am calling DetailFragment and
it is not working ,I see it enters onClickListener but it doesn't go further and it doesn't call DetailFragment
viewBinding.assetImage.setOnClickListener {
val bundle = bundleOf("owner" to "TestUser")
Navigation.createNavigateOnClickListener(R.id.detail_screen,bundle)
}
<fragment
android:id="#+id/home_screen"
android:name="HomeFragment"
android:label="#string/title_home"
tools:layout="#layout/fragment_home">
<action
android:id="#+id/action_home_screen_to_detailFragment"
app:destination="#id/detail_screen" />
</fragment>
<fragment
android:id="#+id/detail_screen"
android:name="DetailFragment"
android:label="DetailFragment"
tools:layout="#layout/fragment_detail">
<argument
android:name="owner"
app:argType="string" />
</fragment>
Below is solution for you,
val bundle = Bundle()
bundle.putString("owner", "TestUser")
if (it.findNavController().currentDestination?.id == R.id.home_screen) {
it.findNavController().navigate(R.id.action_home_screen_to_detailFragment, bundle)
}
You can use safeArgs to pass data.
This can help you
You go to your navigation xml file
Select Fragment to which values are passed.
Add attributes
Build → Rebuild
Then you can use it like this
findNavController().navigate(MenuFragmentDirections.Action_menuFragment_to_servisFragment(param))

Categories

Resources