I have main navigation:
SplashFragment -> RegistrationFragment -> RootFragment
<fragment
android:id="#+id/splashFragment"
android:name="com.low6.low6.features.splash.SplashFragment"
android:label="Splash"
tools:layout="#layout/fragment_splash" >
<action
android:id="#+id/action_next"
app:clearTask="true"
app:destination="#id/registrationFragment" />
</fragment>
<fragment
android:id="#+id/registrationFragment"
android:name="com.low6.low6.features.registration.RegistrationFragment"
android:label="Register">
<action
android:id="#+id/action_next"
app:clearTask="true"
app:destination="#id/rootFragment" />
</fragment>
<fragment
android:id="#+id/rootFragment"
android:name="com.low6.low6.core.RootFragment"
android:label="#string/home"
tools:layout="#layout/fragment_root" />
And I have nested registration navigation:
RegistrationPersonalFragment -> RegistrationContactFragment -> RegistrationSecurityFragment
<fragment
android:id="#+id/registrationPersonalFragment"
android:name="com.low6.low6.features.registration.RegistrationPersonalFragment"
android:label="Register">
<action
android:id="#+id/action_next"
app:destination="#+id/registrationContactFragment" />
</fragment>
<fragment
android:id="#+id/registrationContactFragment"
android:name="com.low6.low6.features.registration.RegistrationContactFragment"
android:label="Register">
<action
android:id="#+id/action_next"
app:destination="#+id/registrationSecurityFragment" />
</fragment>
<fragment
android:id="#+id/registrationSecurityFragment"
android:name="com.low6.low6.features.registration.RegistrationSecurityFragment"
android:label="Register">
<action
android:id="#+id/action_next"
app:destination="#+id/rootFragment" />
</fragment>
How to redirect from the last nested RegistrationSecurityFragment to RootFragment using Jetpack Navigation component?
Currently
<action
android:id="#+id/action_next"
app:destination="#+id/rootFragment" />
And
navigateTo(R.id.action_next)
Gives me
java.lang.IllegalArgumentException: navigation destination com.xxx:id/rootFragment referenced from action com.xxx:id/action_next is unknown to this NavController
at androidx.navigation.NavController.navigate(NavController.java:691)
at androidx.navigation.NavController.navigate(NavController.java:648)
at androidx.navigation.NavController.navigate(NavController.java:634)
at com.xxx.core.BaseFragment.navigateTo(BaseFragment.kt:73)
at com.xxx.core.BaseFragment.navigateTo$default(BaseFragment.kt:66)
at com.xxx.features.registration.RegistrationSecurityFragment$epoxyController$1$$special$$inlined$button$lambda$1.onClick(RegistrationSecurityFragment.kt:106)
at android.view.View.performClick(View.java:6597)
at android.view.View.performClickInternal(View.java:6574)
at android.view.View.access$3100(View.java:778)
at android.view.View$PerformClick.run(View.java:25885)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
If you have more than one navigation graph, please make sure you're using the right navigation controller. Using Navigation.findNavController(view) in some cases you might need to get your root view to get the root's navigation. Hope, this'll help.
When you have nested NavControllers, findNavController() return only last. To get previous navControllers, you can traverse up using parentFragment property.
Extensions with this approach:
// find all nav controllers from closest to farest
fun Fragment.findAllNavControllers(): List<NavController> {
val navControllers = mutableListOf<NavController>()
var parent = parentFragment
while (parent != null) {
if (parent is NavHostFragment) {
navControllers.add(parent.navController)
}
parent = parent.parentFragment
}
return navControllers
}
// find one nav controller by fragment id
fun Fragment.findNavControllerById(#IdRes id: Int): NavController {
var parent = parentFragment
while (parent != null) {
if (parent is NavHostFragment && parent.id == id) {
return parent.navController
}
parent = parent.parentFragment
}
throw RuntimeException("NavController with specified id not found")
}
And usage:
findAllNavControllers()[2]
findNavControllerById(R.id.navHostFragment)
In your code, you can pass the resource ID of the global action to the navigate() method for each UI element.
your_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Navigation.findNavController(view).navigate(R.id.main_fragment);
}
});
Thanks to #Vladimir answer I came up with this solution
val mainNavView = requireActivity().findViewById<View>(R.id.mainNavFragment)
Navigation.findNavController(mainNavView).navigate(R.id.action_next)
You can do it like this ;
in Child Fragment
// parentFragment = getParentFragment() for java
(parentFragment as MyParentFragment).myNavigationHandler(myArguments)
in Parent Fragment
fun myNavigationHandler(myArguments) {
Navigation.findNavController(binding.root)
.navigate(MyFragmentDirections.actionMyAction(myArguments))
}
findNavController().navigate(HostFragmentDirections.actionHostToOtherFragment())
HostFragment is just an example, which should be the name of host of your nested fragment.
For everyone struggling with navigating to somewhere from your nested fragment, this works.
Related
I have problem with navigation component.
I have 2 nav graph: nav1 and nav2
In nav2 start destination by default is FragmentA, but sometimes I need to start from FragmentB. I use this code (this code in MainActivity):
if(isTest) {
val navController = findNavController(navigationFragment())
val navGraph = navController.navInflater.inflate(R.navigation.nav)
navGraph.startDestination = R.id.fragmentB
findNavController(navigationFragment()).graph = navGraph
}
But there is error like this -> java.lang.IllegalArgumentException: No destination with ID 2131362873 is on the NavController's back stack
nav2 code here:
android:id="#+id/nav2"
app:startDestination="#id/FragmentA">
<fragment
android:id="#+id/FragmentA"
android:name="......./FragmentA"
android:label="FragmentA" />
<fragment
android:id="#+id/FragmentB"
android:name="......./FragmentB"
android:label="FragmentB" />
From nav1 action:
<action
android:id="#+id/action_global_link_test"
app:destination="#id/nav2"
app:enterAnim="#anim/enter_from_right"
app:exitAnim="#anim/exit_from_left"
app:popEnterAnim="#anim/enter_from_left"
app:popExitAnim="#anim/exit_from_right" />
I use the navigation component to do various screen transitions.
Pass the title data from A fragment to B fragment at the same time as the screen is switched. (using safe args)
In fragment B, set the data received from A.
And to keep the title data even when the screen is switched, I set it in LiveData in the ViewModel.
But if you go back from fragment B to fragment C,
B's title is missing.
Some say that because this is a replace() method, a new fragment is created every time the screen is switched.
How can I keep the data even when I switch screens in the Navigation Component?
Note: All screen transitions used findNavController.navigate()!
fragment A
startBtn?.setOnClickListener { v ->
title = BodyPartCustomView.getTitle()
action = BodyPartDialogFragmentDirections.actionBodyPartDialogToWrite(title)
findNavController()?.navigate(action)
}
fragment B
class WriteRoutineFragment : Fragment() {
private var _binding: FragmentWritingRoutineBinding? = null
private val binding get() = _binding!!
private val viewModel: WriteRoutineViewModel by viewModels { WriteRoutineViewModelFactory() }
private val args : WriteRoutineFragmentArgs by navArgs() // When the screen changes, it is changed to the default value set in <argument> of nav_graph
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.setValue(args) // set Data to LiveData
viewModel.title.observe(viewLifecycleOwner) { titleData ->
// UI UPDATE
binding.title.text = titleData
}
}
UPDATED 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/nav_graph"
app:startDestination="#id/calendar">
<!-- fragment A -->
<dialog
android:id="#+id/bodyPartDialog"
android:name="com.example.writeweight.fragment.BodyPartDialogFragment"
android:label="BodyPartDialogFragment"
tools:layout="#layout/fragment_body_part_dialog">
<action
android:id="#+id/action_bodyPartDialog_to_write"
app:destination="#id/write"/>
</dialog>
<!-- fragment B -->
<fragment
android:id="#+id/write"
android:name="com.example.writeweight.fragment.WriteRoutineFragment"
android:label="WritingRoutineFragment"
tools:layout="#layout/fragment_writing_routine">
<action
android:id="#+id/action_write_to_workoutListTabFragment"
app:destination="#id/workoutListTabFragment" />
<argument
android:name="title"
app:argType="string"
android:defaultValue="Unknown Title" />
</fragment>
<!-- fragment C -->
<fragment
android:id="#+id/workoutListTabFragment"
android:name="com.example.writeweight.fragment.WorkoutListTabFragment"
android:label="fragment_workout_list_tab"
tools:layout="#layout/fragment_workout_list_tab" >
<action
android:id="#+id/action_workoutListTabFragment_to_write"
app:destination="#id/write"
app:popUpTo="#id/write"
app:popUpToInclusive="true"/>
</fragment>
</navigation>
UPDATED ViewModel(
This is the view model for the B fragment.)
class WriteRoutineViewModel : ViewModel() {
private var _title: MutableLiveData<String> = MutableLiveData()
val title: LiveData<String> = _title
fun setValue(_data: WritingRoutineFragmentArgs) {
_title.value = _data.title
}
}
Error
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.writeweight, PID: 25505
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:612)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at androidx.navigation.NavArgsLazy.getValue(NavArgsLazy.kt:52)
at androidx.navigation.NavArgsLazy.getValue(NavArgsLazy.kt:34)
at com.example.writeweight.fragment.WriteRoutineFragment.getArgs(Unknown Source:4)
at com.example.writeweight.fragment.WriteRoutineFragment.onViewCreated(WriteRoutineFragment.kt:58)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:2987)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:546)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2189)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2106)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2002)
at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:524)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8512)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
Caused by: java.lang.IllegalArgumentException: Required argument "title" is missing and does not have an android:defaultValue
at com.example.writeweight.fragment.WriteRoutineFragmentArgs$Companion.fromBundle(WriteRoutineFragmentArgs.kt:26)
at com.example.writeweight.fragment.WriteRoutineFragmentArgs.fromBundle(Unknown Source:2)
A single ViewModel can also be used for multiple fragments. Fragments are obviously showing inside an activity. The ViewModel in the activity can be passed to each fragment, that has the reference from the title. It is the solution if you want to solve using ViewModel.
Otherwise you can try the savedInstance method for solving this issue. Here is a thread about it.
Following from my comment:
I would make the title argument nullable and pass null as the default value. Then in viewModel.setValue(), ignore it if it's null instead of passing it to the LiveData.
The ViewModel's setValue() function should look like:
fun setValue(_data: WritingRoutineFragmentArgs) {
_data.title?.let { _title.value = it }
}
so the value is only passed along to the LiveData if it is not the default (null).
Your xml for the value should mark the default as #null and needs to have nullable="true". Your stack trace looks like there was a problem with how you specified the default or making it nullable.
<argument
android:name="title"
app:argType="string"
app:nullable="true"
android:defaultValue="#null" />
IMO, for proper separation of concerns, the ViewModel should not have any awareness of navigation arguments. The setValue function should take a String parameter, and you should decide in the fragment whether to update the ViewModel. Like this:
// In ViewModel
fun setNewTitle(title: String) {
_title.value = title
}
// in Fragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
args.title?.let { viewModel.setNewTitle(it) } // set Data to LiveData
viewModel.title.observe(viewLifecycleOwner) { titleData ->
// UI UPDATE
binding.title.text = titleData
}
}
I want to send my arguments to another fragment using NavDirections
<fragment
android:id="#+id/conformationFragment"
android:name="com.example.panoramic.ui.ConformationFragment"
android:label="ConformationFragment"
tools:layout="#layout/fragment_conformation">
<action
android:id="#+id/action_conformationFragment_to_registerProductFragment"
app:destination="#id/registerProductFragment" />
<action
android:id="#+id/action_conformationFragment_to_homeFragment"
app:destination="#id/homeFragment" />
<argument
android:name="modelNumber"
app:argType="string" />
<argument
android:name="serialNumber"
app:argType="string" />
</fragment>
Here is how I attempt to send arguments:
class ConformationFragment : Fragment(R.layout.fragment_conformation) {
private var fragmentConformationBinding: FragmentConformationBinding? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentConformationBinding.bind(view)
fragmentConformationBinding = binding
val modelNumber = binding.modelValue.text.toString()
val serialNumber = binding.serialValue.text.toString()
binding.editingInformationButton.setOnClickListener {
findNavController().navigate(R.id.action_conformationFragment_to_registerProductFragment)
}
binding.confirmButton.setOnClickListener {
findNavController().navigate(ConformationFragmentDirections
.actionConformationFragmentToHomeFragment(modelNumber, serialNumber))
}
}
override fun onDestroyView() {
fragmentConformationBinding = null
super.onDestroyView()
}
}
I am sure dependencies are correct, but still, the error appears:
Too many arguments for public final fun actionConformationFragmentToHomeFragment(): NavDirections defined in com.example.panoramic.ui.ConformationFragmentDirections.Companion
and red line under modelNumber and serialNumber in actionConformationFragmentToHomeFragment() function
I checked other similar question but none of them works for me.
The issue is that the arguments you defined in your navigation graph file are accepted by ConformationFragment and not by HomeFragment.
Arguments should be defined under <fragment> tag of a fragment that is accepting defined arguments, so every other navigation action to this fragment would accept arguments. This is how the navigation graph should be declared regarding ConformatioFragment and HomeFragment:
<fragment
android:id="#+id/conformationFragment"
android:name="com.example.panoramic.ui.ConformationFragment"
android:label="ConformationFragment"
tools:layout="#layout/fragment_conformation">
<action
android:id="#+id/action_conformationFragment_to_registerProductFragment"
app:destination="#id/registerProductFragment" />
<action
android:id="#+id/action_conformationFragment_to_homeFragment"
app:destination="#id/homeFragment" />
</fragment>
<fragment
android:id="#+id/homeFragment"
android:name="com.example.panoramic.ui.HomeFragment"
android:label="HomeFragment"
tools:layout="#layout/fragment_home">
<argument
android:name="modelNumber"
app:argType="string" />
<argument
android:name="serialNumber"
app:argType="string" />
</fragment>
After these changes, you should be able to pass arguments into actionConformationFragmentToHomeFragment() function.
I have implemented navigation Drawer with Navigation Components in Android. I have 5 fragments that I want to go back to my HomeFragment when I click on back pressed. For the moment they stay onBackStack and do not go to my desired fragment but go to whatever fragment was first.
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"
app:startDestination="#id/setupFragment"
android:id="#+id/treasure_nav"
android:label="Pick a country">
<fragment android:id="#+id/homeFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.home.HomeFragment"
android:label="Home"
tools:layout="#layout/fragment_home">
<action android:id="#+id/action_home_fragment_to_namesFragment2"
app:popUpTo="#id/homeFragment"
app:destination="#id/namesFragment"/>
<action android:id="#+id/action_home_fragment_to_quranFragment"
app:popUpTo="#id/homeFragment"
app:destination="#id/quranFragment"/>
<action android:id="#+id/action_homeFragment_to_tasbeehFragment"
app:popUpTo="#id/homeFragment"
app:destination="#id/tasbeehFragment"/>
<action android:id="#+id/action_homeFragment_to_galleryFragment"
app:popUpTo="#id/homeFragment"
app:destination="#id/galleryFragment"/>
<action android:id="#+id/action_homeFragment_to_newsFragment"
app:popUpTo="#id/homeFragment"
app:destination="#id/newsFragment"/>
<action android:id="#+id/action_homeFragment_to_settingsFragment"
app:popUpTo="#id/homeFragment"
app:destination="#id/settingsFragment"/>
</fragment>
<fragment
android:id="#+id/namesFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.names.NamesFragment"
android:label="Names of Allah"
tools:layout="#layout/fragment_names"/>
<fragment
android:id="#+id/quranFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.quran.QuranFragment"
android:label="Quran"
tools:layout="#layout/fragment_quran"/>
<fragment android:id="#+id/tasbeehFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.tasbeeh.TasbeehFragment"
android:label="Tasbeeh"
tools:layout="#layout/fragment_tasbeeh"/>
<fragment android:id="#+id/galleryFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.gallery.GalleryFragment"
android:label="Gallery"
tools:layout="#layout/fragment_gallery"/>
<fragment android:id="#+id/newsFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.news.NewsFragment"
android:label="News"
tools:layout="#layout/fragment_news"/>
<fragment android:id="#+id/settingsFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.settings.SettingsFragment"
android:label="Settings"
tools:layout="#layout/fragment_settings"/>
<fragment android:id="#+id/setupFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.setup.SetupFragment"
android:label="Pick country"
tools:layout="#layout/fragment_setup">
<action android:id="#+id/action_setupFragment_to_homeFragment3"
app:destination="#+id/homeFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/treasure_nav"
app:popUpToInclusive="true"/>
</fragment>
</navigation>
And this is my onBackPressed in my MainActivity (and the only one) :
override fun onBackPressed() {
if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
drawer_layout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
Edit: When i remove the super.onBackPressed() and replace it with :
findNavController(R.id.nav_host_fragment).popBackStack(R.id.homeFragment, false) I achieve what I want. The only problem is that when I am in the homeFragment I want to end the app but I can't.
If my understanding is correct, you want to go back to HomeFragment wherever you are in the navigation flow. For this case you could try registering OnBackPressedCallback on your Fragments via addOnBackPressedCallback, and call popBackStack to navigate to your HomeFragment. Try adding this to Fragments' onViewCreated that need to go back to HomeFragment on backpress:
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
navController = Navigation.findNavController(view);
requireActivity().addOnBackPressedCallback(getViewLifecycleOwner(), () -> {
navController.popBackStack(R.id.homeFragment, false);
});
return true;
});
If you want to close app when press back in HomeFragment, it's just specified these attributes of the last action that navigates you to this destination:
app:popUpToInclusive to true
app:popUpTo to of the last fragment(SetupFragment) that navigates you here(HomeFragment)
It means change your code like this:
<fragment android:id="#+id/setupFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.setup.SetupFragment"
android:label="Pick country"
tools:layout="#layout/fragment_setup">
<action android:id="#+id/action_setupFragment_to_homeFragment3"
app:destination="#+id/homeFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/setupFragment" // this line changes
app:popUpToInclusive="true" /> // and this requires too
</fragment>
Is it possible to pass and access arguments in a fragment using a bottom navigation view and the Navigation component?
I'm using a one activity with many fragments approach where my top level fragment requires an argument(Usually done via the newInstance generated method). I've had a look at the Navigation component developer guide and the codelab but it only mentions using safeargs and adding argument tags in the destinations and actions.
Here's my navigation graph:
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
app:startDestination="#id/homeFragment">
<fragment android:id="#+id/homeFragment"
android:name="uk.co.homeready.homeready.HomeFragment"
android:label="fragment_home"
tools:layout="#layout/fragment_home">
<!--Do I create an argument block here?-->
</fragment>
<fragment android:id="#+id/calculatorFragment"
android:name="uk.co.homeready.homeready.CalculatorFragment"
android:label="fragment_calculator"
tools:layout="#layout/fragment_calculator"/>
<fragment android:id="#+id/resourcesFragment"
android:name="uk.co.homeready.homeready.ResourcesFragment"
android:label="fragment_resources"
tools:layout="#layout/fragment_resources"/>
</navigation>
Bottom Navigation View menu:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/homeFragment"
android:icon="#drawable/ic_home_black_24dp"
android:title="#string/title_home"/>
<item
android:id="#+id/calculatorFragment"
android:icon="#drawable/ic_baseline_attach_money_24px"
android:title="#string/title_calculator"/>
<item
android:id="#+id/resourcesFragment"
android:icon="#drawable/ic_baseline_library_books_24px"
android:title="#string/title_resources"/>
</menu>
MainActivity:
override fun onCreate(savedInstanceState: Bundle?) {
...
val navController = Navigation.findNavController(this,
R.id.nav_host_fragment)
bottom_navigation.setupWithNavController(navController)
....
}
activity_main.xml
<android.support.constraint.ConstraintLayout>
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:layout_constraintBottom_toTopOf="#id/bottom_navigation"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph"/>
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_navigation"
app:menu="#menu/bottom_navigation"/>
</android.support.constraint.ConstraintLayout>
HomeFragment
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val argument = //TODO access argument here
...
}
If I understood you correctly, you want to pass arguments to destinations that is tied to menu items. Try to use 'OnDestinationChangedListener' inside your activity onCreate method, something like this:
navController.addOnDestinationChangedListener { controller, destination, arguments ->
when(destination.id) {
R.id.homeFragment -> {
val argument = NavArgument.Builder().setDefaultValue(6).build()
destination.addArgument("Argument", argument)
}
}
}
Update:
If you want that your start destination will receive default arguments the implementation should be different.
First, remove 'app:navGraph="#navigation/nav_graph"' from your 'NavHostFragment' xml tag.
Then, inside your activity onCreate you need to inflate the graph:
val navInflater = navController.navInflater
val graph = navInflater.inflate(R.navigation.nav_graph)
Then add your arguments to graph(this arguments will be attached to start destination)
val navArgument1=NavArgument.Builder().setDefaultValue(1).build()
val navArgument2=NavArgument.Builder().setDefaultValue("Hello").build()
graph.addArgument("Key1",navArgument1)
graph.addArgument("Key2",navArgument2)
Then attach the graph to NavController:
navController.graph=graph
Now your first destination should receive the attached arguments.
The correct way to do this is indeed with an <argument> block on your destination.
<fragment android:id="#+id/homeFragment"
android:name="uk.co.homeready.homeready.HomeFragment"
android:label="fragment_home"
tools:layout="#layout/fragment_home">
<argument
android:name="Argument"
android:defaultValue="value"
/>
</fragment>
This will automatically populate the arguments of the Fragment with the default value without any additional code needed. As of Navigation 1.0.0-alpha09, this is true whether you use the Safe Args Gradle Plugin or not.
Default values was not usable for me, because I have dynamic menu items that could have multiple of the same destination with different arguments. (changed from server)
Implement BottomNavigationView.OnNavigationItemSelectedListener:
override fun onNavigationItemSelected(item: MenuItem): Boolean {
val fragmentId = item.itemId
val arguments = argumentsByFragmentId[fragmentId] // custom mutableMapOf<Int, Bundle?>() with arguments
navController().navigate(fragmentId, arguments)
return true
}
To use that you will takeover the navigation, by replacing the listener. The order of calls here are important:
bottomNavigationView.setupWithNavController(navController)
bottomNavigationView.setOnNavigationItemSelectedListener(this)