i have a problem with navigation between fragments in Navbar.
This is my situation:
I have 3 main fragments: Home, List, Settings
Home has 2 sub fragment: Add & Edit
So, if i navigate between the 3 main fragments with navbar is everything ok, then if i go in one of 2 "sub fragments" and then, (instead of "navigate up" before and then change main) i directly choose one of other 2 "main" i will go there, but then if i come back to List i will see the sub fragment.
I have to Navigate Up to see again List.
**What i want is **if i switch main fragment when im inside of one of the "subs"(son of List), and then again i choose List, i want see List and not the sub that i left.
My Code:
Navigation
<fragment
android:id="#+id/homeFragment"
android:name="com.somi.fidelitycardconnect.ui.home.HomeFragment"
android:label="Home"
tools:layout="#layout/fragment_home" />
<fragment
android:id="#+id/settingsFragment"
android:name="com.somi.fidelitycardconnect.ui.settings.SettingsFragment"
android:label="Impostazioni"
tools:layout="#layout/fragment_settings" />
<fragment
android:id="#+id/listFragment"
android:name="com.somi.fidelitycardconnect.ui.list.ListFragment"
android:label="Lista clienti"
tools:layout="#layout/fragment_list">
<action
android:id="#+id/action_listFragment_to_addFormFragment"
app:destination="#id/addFormFragment" />
<action
android:id="#+id/action_listFragment_to_editFormFragment"
app:destination="#id/editFormFragment" />
</fragment>
<fragment
android:id="#+id/addFormFragment"
android:name="com.somi.fidelitycardconnect.ui.form.AddFormFragment"
android:label="Aggiungi cliente"
tools:layout="#layout/fragment_add_form">
</fragment>
<fragment
android:id="#+id/editFormFragment"
android:name="com.somi.fidelitycardconnect.ui.form.EditFormFragment"
android:label="Informazioni cliente"
tools:layout="#layout/fragment_edit_form">
</fragment>
MainActivity:
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val navView: BottomNavigationView = binding.navView
val navController = findNavController(R.id.nav_host_fragment_activity_main)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.homeFragment,
R.id.listFragment,
R.id.settingsFragment
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment_activity_main)
return navController.navigateUp(appBarConfiguration)
|| super.onSupportNavigateUp()
}
}
enter image description here
enter image description here
**What i want is **if i switch main fragment when im inside of one of the "subs"(son of List), and then again i choose List, i want see List and not the sub that i left.
If you want to press one of the bottomNav items and start with the default position, you need to pop the other destinations. Use below code to achieve it:
navView.setupWithNavController(navController)
navView.setOnItemSelectedListener { item ->
NavigationUI.onNavDestinationSelected(item, navController)
navController.popBackStack(item.itemId, inclusive = false)
true
}
I'm trying to do solve some navigation problems of BottomNavigationView using Jetpack Navigation. I'm using Jetpack Navigation 2.4.0-beta02. The first problem is the back button always navigates me back to the start destination. This one is solved by adding menuCategory: secondary. However, there is another problem.
Let say I have 4 navigation menus of BottomNavigationView, fragment A, B, C, and D. And then, there is another fragment A1 which is not part of the BottomNavigationView. The nav graph looks like this:
<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/mobile_navigation"
app:startDestination="#+id/navigation_a">
<fragment
android:id="#+id/navigation_a"
android:name="com.example.FragmentA"
android:label=""
tools:layout="#layout/fragment_a">
<action
android:id="#+id/action_navigation_a_to_navigation_a1"
app:destination="#id/navigation_a1"
app:launchSingleTop="true" />
</fragment>
<fragment
android:id="#+id/navigation_a1"
android:name="com.example.FragmentA1"
android:label=""
tools:layout="#layout/fragment_a1" />
<fragment
android:id="#+id/navigation_b"
android:name="com.example.FragmentB"
android:label=""
tools:layout="#layout/fragment_b" />
<fragment
android:id="#+id/navigation_c"
android:name="com.example.FragmentC"
android:label=""
tools:layout="#layout/fragment_c" />
<fragment
android:id="#+id/navigation_d"
android:name="com.example.FragmentD"
android:label=""
tools:layout="#layout/fragment_d" />
</navigation>
So, if I navigate from fragment A to A1, and then to fragment B (now the active menu indicator is at 2nd menu), then I press the hardware back button, it shows me fragment A1 which is correct. But, the active menu indicator is still at the 2nd menu instead of the 1st menu. I expected the active menu indicator at the 1st menu because I navigate to fragment A1 from fragment A.
This is my menu.xml file
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/navigation_a"
android:title="A"
android:menuCategory="secondary" />
<item
android:title="B"
android:id="#+id/navigation_b"
android:menuCategory="secondary" />
<item
android:title="C"
android:id="#+id/navigation_c"
android:menuCategory="secondary" />
<item
android:title="D"
android:id="#+id/navigation_d"
android:menuCategory="secondary" />
</menu>
and MainActivity.kt
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment_activity_main) as NavHostFragment
navController = navHostFragment.navController
binding.navView.setupWithNavController(navController)
Thanks for the help!
Now the BottomNavigationView has 4 fragments: A, B, C, and D.
And you want to make the navigation like:
A (A highlighted) -> A1 -> B (A highlighted) -> Back pressed -> A1 (A highlighted)
But you got:
A (A highlighted) -> A1 -> B (A highlighted) -> Back pressed -> A1 (B highlighted)
So, you need to have A highlighted when you return back from B.
To do that you need to examine the previous fragment in the back stack when the back pressed at fragment B.
pseudo code:
// At fragment B
if (back_pressed) {
if (previous_fragment is A1) {
popup the back stack twice to return back to A
} else {
popup the back stack once (Normal behavior of the back stack)
}
}
Code:
class FragmentB : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_b, container, false)
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
object : OnBackPressedCallback(true) {
override
fun handleOnBackPressed() { // Back pressed
val previousFragment = findNavController().previousBackStackEntry?.destination?.id
previousFragment?.let {
when (previousFragment) {
R.id.navigation_a1 ->
findNavController().navigateUp()
else ->
Log.d(TAG, "onCreateView: Not A1")
}
findNavController().navigateUp() // Normal behaviour when back is pressed
}
}
})
return view
}
}
Side Note:
This will work for the current navGraph setup; but I encourage you to have another graph, or even a nested navigation inside the current one; so that you keep the fragment A1 separated from the BottomNavView fragments.
UPDATE:
I want when pressing back, it still shows fragment A1 and the indicator is at the 1st menu. Your solution instead navigates me to fragment A.
To do so, you'd normally popup the back stack only once, and set the BottomNavView menu item to be checked:
Assuming the BottomNavView is hosted only by the activity, so you can access it with requireActivity() as MainActivity; cast that to the name of your activity.
Or if it's hosted by a fragment, then use parentFragment as MyParentFragment, similarly cast that to the name of the parent fragment.
At activity or parent fragment:
fun highlightAItem() {
// Highlight A item from the bottomNavView
val item = navView.menu.findItem(R.id.navigation_a)
item.isChecked = true
}
At fragment B:
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
object : OnBackPressedCallback(true) {
override
fun handleOnBackPressed() {
// Highlight A item from the BottomNavView
(requireActivity() as MainActivity).highlightAItem()
// Pop backstack once to return to A1 fragment
findNavController().navigateUp()
}
})
I am using 3 fragments with 1 activity for user profile features. I use navigation to move to fragments:
The problem is when I go from profileFragment to editProfileFragment, change user's data and then go back to profileFragment by navigation action, I cannot reach editProfileFragment again from profileFragment. I got this error:
Navigation action/destination xxx:id/action_profileFragment_to_editProfileFragment cannot be found from the current destination Destination(xxx:id/editProfileFragment)
I am trying to use MVVM architecture so my navigation goes like this - in fragment I observe LiveData:
navController = Navigation.findNavController(view)
viewModel.navigateTo.observe(viewLifecycleOwner, EventObserver {
navController.navigate(it)
})
viewModel 'navigateTo':
private val _navigateTo = MutableLiveData<Event<Int>>()
val navigateTo: LiveData<Event<Int>> = _navigateTo
and the navigation methods:
fun goBackToProfile(){
_navigateTo.value = Event(R.id.action_editProfileFragment_to_profileFragment)
}
fun editProfileButtonClick() {
_navigateTo.value = Event(R.id.action_profileFragment_to_editProfileFragment)
}
I also use Event wrapper class by Jose Alcerreca:
open class Event<out T>(private val content: T) {
var hasBeenHandled = false
private set // Allow external read but not write
/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}
class EventObserver<T>(private val onEventUnhandledContent: (T) -> Unit) : Observer<Event<T>> {
override fun onChanged(event: Event<T>?) {
event?.getContentIfNotHandled()?.let {
onEventUnhandledContent(it)
}
}
}
I don't know if error occurs because of Event wrapper or I do something wrong with navigation. I will appreciate any advices.
EDIT:
navigation.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/main_nav_graph"
app:startDestination="#id/homeFragment">
<fragment
android:id="#+id/homeFragment"
android:name="xxx.mainPackage.views.HomeFragment"
android:label="fragment_home"
tools:layout="#layout/fragment_home" />
<fragment
android:id="#+id/profileFragment"
android:name="xxx.mainPackage.views.ProfileFragment"
android:label="fragment_profile"
tools:layout="#layout/fragment_profile" >
<action
android:id="#+id/action_profileFragment_to_editProfileFragment"
app:destination="#id/editProfileFragment" />
</fragment>
<fragment
android:id="#+id/editProfileFragment"
android:name="xxx.mainPackage.views.EditProfileFragment"
android:label="fragment_edit_profile"
tools:layout="#layout/fragment_edit_profile" >
<action
android:id="#+id/action_editProfileFragment_to_chooseImageFragment"
app:destination="#id/chooseImageFragment" />
<action
android:id="#+id/action_editProfileFragment_to_profileFragment"
app:destination="#id/profileFragment" />
</fragment>
<fragment
android:id="#+id/chooseImageFragment"
android:name="xxx.mainPackage.views.ChooseImageFragment"
android:label="ChooseImageFragment" >
<action
android:id="#+id/action_chooseImageFragment_to_editProfileFragment"
app:destination="#id/editProfileFragment" />
</fragment>
</navigation>
It looks to me like you're navigating the wrong way.
Firstly
You are moving from ProfileFragment to EditProfileFragment. Then you move from EditProfileFragment to ProfileFragment via action whose id is R.id.action_profileFragment_to_editProfileFragment.
That action I see you define from Fragment ProfileFragment, not EditFragment.
Make sure your LiveData navigateTo in EditProfileFragment trigger id is R.id.action_editProfileFragment_to_profileFragment when you want to open ProfileFragment from EditProfileFragment.
Secondly
When you return from EditProfileFragment , don't call navController.navigate() as it will keep your current fragment in the backstack and push your target fragment to the backstack. If you want to go back without keeping your current fragment in the backstack, call navController.popBackStack().
Error is very clear, there is no action_profileFragment_to_editProfileFragment defined on editProfileFragment. if you look at your navigation xml, you can see that action_profileFragment_to_editProfileFragment is defined for ProfileFragment.
But you are trying to use this navigation action from EditProfileFragment, which causes the error. so either update your navigation to include this action for EditProfileFragment or use some action which is already defined for EditProfileFragment.
I'm implementing this bottom navigation pattern but with a splash fragment.
My issue is when I navigate throw different fragments with bottom menu and I press to go back, I don't go back to the home fragment, instead of this, I return to the previous fragment.
For example, I have fragments A-B-C:
Now I'm on fragment A and I press to go to B.
Then I press to go to C (from B).
Then I press to go back.
The result is I'm getting back to B, not to fragment A (what I really want!).
(If I set in the navigation graph app:startDestination -> fragment A - instead of login fragment - everything goes well).
Here is my graph:
<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/main_navigation"
app:startDestination="#id/splashFragment">
<fragment
android:id="#+id/splashFragment"
android:name="application.SplashFragment"
tools:layout="#layout/fragment_splash">
<action
android:id="#+id/action_splashFragment_to_fragmentA"
app:destination="#id/fragmentA"
app:popUpTo="#id/main_navigation"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/fragmentA"
android:name="application.fragmentA"
android:label="#string/fragmentA"
tools:layout="#layout/fragmentA" />
<fragment
android:id="#+id/fragmentB"
android:name="application.fragmentB"
android:label="fragmentB"
tools:layout="#layout/fragmentB" />
<fragment
android:id="#+id/fragmentC"
android:name="application.fragmentC"
android:label="#string/fragmentC"
tools:layout="#layout/fragmentC" />
And here my MainActivity:
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Toolbar & Navigation
setSupportActionBar(toolbar)
navController = findNavController(R.id.nav_host)
// AppBarConfiguration with the correct top-level destinations
appBarConfiguration = AppBarConfiguration(
topLevelDestinationIds = setOf(
R.id.fragmentA,
R.id.fragmentB,
R.id.fragmentC
)
)
// This allows NavigationUI to decide what label to show in the action bar
// By using appBarConfig, it will also determine whether to
// show the up arrow or drawer menu icon
setupActionBarWithNavController(navController, appBarConfiguration)
// Set destinations to left and bottom navigation
bottom_navigation.setupWithNavController(navController)
// Set visibility for NavigationView & Toolbar
visibilityNavElements()
}
// Allows NavigationUI to support proper up navigation or the drawer layout
// drawer menu, depending on the situation
override fun onSupportNavigateUp() = navController.navigateUp(appBarConfiguration)
private fun visibilityNavElements() {
findNavController(R.id.nav_host).addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
R.id.splashFragment -> {
toolbar.visibility = View.GONE
bottom_navigation.visibility = View.GONE
}
else -> {
toolbar.visibility = View.VISIBLE
bottom_navigation.visibility = View.VISIBLE
}
}
}
}
}
Thanks!
You can handle OnbackPress in any fragment like below and is the recommended way . You can use it in Fragment C and when back is pressed you can navigate to Fragment A
in Java
OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) {
#Override
public void handleOnBackPressed() {
}
};
requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
in Kotlin
val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
// Handle the back button event
}
What I have done:
I have created Navigation Drawer Activity, As updated new format of Navigation Drawer Activity, As per new Android architecture, I got it with Navigation Component structure.
The NavigationView code with NavController and NavigationUI is below which is opening fragment when I click on any navigation item.
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_profile, R.id.nav_privacy_policy,
R.id.nav_terms, R.id.nav_contact_us, R.id.nav_share, R.id.nav_send)
.setDrawerLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
This is for nav_host_fragment:
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/mobile_navigation" />
The navigation is happening using this navigation/mobile_navigation.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/mobile_navigation"
app:startDestination="#+id/nav_home">
<fragment
android:id="#+id/nav_home"
android:name="com.sohamerp.marsremedies.fragment.HomeFragment"
android:label="#string/menu_home"
tools:layout="#layout/fragment_home" />
<fragment
android:id="#+id/nav_profile"
android:name="com.sohamerp.marsremedies.fragment.ProfileFragment"
android:label="#string/menu_my_profile"
tools:layout="#layout/fragment_profile" />
<fragment
android:id="#+id/nav_privacy_policy"
android:name="com.sohamerp.marsremedies.fragment.PrivacyPolicyFragment"
android:label="#string/menu_privacy_policy"
tools:layout="#layout/fragment_privacy_policy" />
<fragment
android:id="#+id/nav_terms"
android:name="com.sohamerp.marsremedies.fragment.TermsConditionFragment"
android:label="#string/menu_terms"
tools:layout="#layout/fragment_terms_condition" />
<fragment
android:id="#+id/nav_contact_us"
android:name="com.sohamerp.marsremedies.fragment.ContactUsFragment"
android:label="#string/menu_contact_us"
tools:layout="#layout/fragment_terms_condition" />
</navigation>
What I want to do:
Now I want to pass some values as a bundle (arguments) in Fragment when it's called.
Scenario: I have two fragments PrivacyPolicyFragment and TermsConditionsFragment, In both fragments, I am just opening links inside WebView accordingly. So When I click on the menu item of Privacy Policy, I will pass a link related to the same.
In this new structure navigation/mobile_navigation.xml opening fragments, How can I pass arguments?
Any help?
So I forgot to go through this link : Define Destination Arguments
But this answer helpful to all lazy peoples like me:
Add dependency in project level build.gradle:
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.1.0"
Apply plugin in app level build.gradle:
apply plugin: "androidx.navigation.safeargs"
Using XML: predefined (static) value:
In xml file of navigation /navigation/mobile_navigation.xml declare argument tag as below or you can design through this link:
<fragment
android:id="#+id/nav_privacy_policy"
android:name="com.sohamerp.marsremedies.fragment.PrivacyPolicyFragment"
android:label="#string/menu_privacy_policy"
tools:layout="#layout/fragment_privacy_policy" >
<argument
android:name="privacyPolicyLink"
app:argType="string"
android:defaultValue="http://sohamerp.com/avo/avo_privacy_policy.html"/>
</fragment>
<fragment
android:id="#+id/nav_terms"
android:name="com.sohamerp.marsremedies.fragment.PrivacyPolicyFragment"
android:label="#string/menu_terms"
tools:layout="#layout/fragment_terms_condition" >
<argument
android:name="privacyPolicyLink"
app:argType="string"
android:defaultValue="http://sohamerp.com/avo/avo_privacy_policy.html"/>
</fragment>
Now you have to write code in your Fragment like:
if(getArguments() != null) {
// The getPrivacyPolicyLink() method will be created automatically.
String url = PrivacyPolicyFragmentArgs.fromBundle(getArguments()).getPrivacyPolicyLink();
}
Hope it will helps you others.
Simple and fast solution:
pass arguments between destinations
Bundle bundle = new Bundle();
bundle.putString("amount", amount);
Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);
and receiving
TextView tv = view.findViewById(R.id.textViewAmount);
tv.setText(getArguments().getString("amount"));
In this scenario, you can use
private NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
// Create the Bundle to pass, you can put String, Integer, or serializable object
Bundle bundle = new Bundle();
bundle.putString("link","http://yourlink.com/policy");
bundle.putSerializable("USER", user); // Serializable Object
navController.navigate(R.id.nav_terms, bundle); // called fragment with agruments
In case of any help you can reply on it
To pass arguments to other Fragments/Destinations, use Safe Args which ensures type safety. Just like #bromden illustrated, Safe Args will generate a class for each fragment/destination where an action originates. You can then pass the arguments into the action that navigates to the Fragments.
In the receiving fragment, say PrivacyFragment if your code is in Kotlin, use by navArgs() property delegate to access the arguments. i.e.
val args: PrivacyFragmentArgs by navArgs()
To better understand this, visit Pass data between destinations
In newer version of Android Studio 3.2+, below dependency and plug-in need to add in both build.gradle file
Step-1
Add dependency in Project-Level build.gradle
dependencies {
classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5'
}
Apply plugins in App-Level build.gradle
plugins {
id 'androidx.navigation.safeargs'
}
Step-2
In Navigation file, res/navigation/nav_graph.xml
Declare argument tag in any fragment or inner fragment with action tag
List item
Sample xml code
<fragment
android:id="#+id/nav_register"
android:name="com.pd.demo.ui.profile.RegisterFragment"
android:label="#string/title_register"
tools:layout="#layout/fragment_register">
<action
android:id="#+id/action_nav_register_to_nav_verify_otp"
app:destination="#id/nav_verify_otp">
<argument
android:name="mobile"
app:argType="string" />
<argument
android:name="password"
app:argType="string" />
</action>
</fragment>
Step-3
Below Kotlin code, pass argument to destination fragment
val bundle = bundleOf("mobile" to binding.etMobileNo.text.toString().trim())
Navigation.findNavController(binding.root).navigate(R.id.action_nav_register_to_nav_verify_otp, bundle)
Step-4
Below Kotlin code, get bundle argument from source fragment
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
mobileNo = arguments!!.getString("mobile").toString()
password = arguments!!.getString("password").toString()
}
This code will helps
You could implement NavigationView.OnNavigationItemSelectedListener
And do something like this:
override fun onNavigationItemSelected(item: MenuItem): Boolean {
drawer_layout.closeDrawers()
if (item.itemId == nv_navigation_drawer_navigation_view.checkedItem?.itemId)
return false
Handler().postDelayed({
when (item.itemId) {
R.id.nav_privacy_policy -> {
val action = FragmentDirections.actionFragmentToPrivacyFragment("Policy link")
findNavController().navigate(action)
}
}
}, DRAWER_NAVIGATION_DELAY)
return true
}
And in xml you can add argument to the recieving fragment, in this case
<fragment
android:id="#+id/nav_privacy_policy"
android:name=".fragment.PrivacyPolicyFragment"
android:label="#string/menu_privacy_policy"
tools:layout="#layout/fragment_privacy_policy">
<argument
android:name="policy"
app:argType="string" />
</fragment>
You can also pass serializable objects, enum values and arrays of primitive types.
For example:
enum class ObjectType : Serializable {
FIRST, SECOND
}
Then, add arguments to the xml
<fragment
android:id="#+id/nav_profile"
android:name="com.sohamerp.marsremedies.fragment.ProfileFragment"
android:label="#string/menu_my_profile"
tools:layout="#layout/fragment_profile" >
<argument
android:name="myObjectType"
android:defaultValue="SECOND"
app:argType="com.project.app.data.ObjectType" />
</fragment>
Note, that you should specify complete path!
Passing data from the start destination with NavController NavGraph navigate is straightforward. I use this to display order lines associated to an order header:
private void showRepositionLinesFragment(AppObjects.RepOrderHeader orderHeader) {
int number = orderHeader.getOrderNumber();
String orderNumber = String.format("%06d",number);
String createDate = orderHeader.getCreateDate();
Globals.LogTrace(this, AppAlertDialog.DialogType.Info,
"Navigate to FragRepoLines with orderNumber: " + orderNumber,false);
NavController navController = NavHostFragment.findNavController(FragmentRepositionHeaders.this);
Bundle bundle = new Bundle();
bundle.putString(getString(R.string.arg_header_ordernumber),orderNumber);
bundle.putString(getString(R.string.arg_repheader_createdate), createDate);
navController.getGraph().findNode(R.id.FragRepoLines).setLabel(orderNumber + " " + createDate);
navController.navigate(R.id.action_FragRepoHeaders_to_FragRepoLines,bundle);
}
Getting data from the fragment that handles the order lines turned to be more complicated. Tried for hours with NavController getArguments().
In the end this is what worked for me.
In the start fragment:
NavController navController = NavHostFragment.findNavController(this);
// We use a String here, but any type that can be put in a Bundle is supported
MutableLiveData<String> liveData = navController.getCurrentBackStackEntry()
.getSavedStateHandle()
.getLiveData(getString(R.string.arg_header_ordernumber));
liveData.observe(getViewLifecycleOwner(), new Observer<String>() {
#Override
public void onChanged(String s) {
Globals.LogTrace(this, AppAlertDialog.DialogType.Info, "+++++++++ liveData changed -> " + s, false);
}
});
In the destination fragment:
String arg = getString(R.string.arg_header_ordernumber);
NavController navController = NavHostFragment.findNavController(this);
NavBackStackEntry navBackStackEntry = navController.getCurrentBackStackEntry();
if (navBackStackEntry != null) {
SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle();
if (savedStateHandle != null) {
savedStateHandle.set(arg, "000000");
} else {
Globals.LogTrace(this, AppAlertDialog.DialogType.Info,"savedStateHandle == null",false);
}
} else {
Globals.LogTrace(this, AppAlertDialog.DialogType.Info,"navBackStackEntry == null",false);
}
Source: Interact programmatically with the Navigation component
I changed the navController.getPreviousBackStackEntry() for navController.getCurrentBackStackEntry()
I had the same issue but I´m still not able to pass the arguments using fragment directions. Since I need the value in several of my fragments I decided to use a companion object in my main activity. It´s probably not the best but it solves the problem:
class MainActivity : AppCompatActivity() {
companion object{
var myGlobalVar = "Example"
}
override fun onCreate(savedInstanceState: Bundle?) {....
Then I can access its value in all of my fragments by importing it:
import myAppPackage.MainActivity.Companion.myGlobalVar
I had to delete the argument from my navGraph but i can still access it in the background.