42

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?

kalucki23
  • 172
  • 4
  • 15
Pratik Butani
  • 60,504
  • 58
  • 273
  • 437

11 Answers11

35

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"));
sina akbari
  • 618
  • 6
  • 7
26

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.

Pratik Butani
  • 60,504
  • 58
  • 273
  • 437
  • Alternatively, to generate Kotlin code suitable for Kotlin-only modules add: `apply plugin: "androidx.navigation.safeargs.kotlin" ` – Anoop M Maddasseri Sep 20 '20 at 13:11
  • 1
    can we use bundle to pass data? – sophin Nov 22 '20 at 12:38
  • 5
    how can you pass data? like from here:`Navigation.findNavController(view).navigate(R.id.action);` – G. Ciardini Apr 18 '21 at 19:44
  • @sophin From the docs: "Safe Args is strongly recommended for navigating and passing data, because it ensures type-safety. In some cases, for example if you are not using Gradle, you can't use the Safe Args plugin. In these cases, you can use Bundles to directly pass data." – MGLabs Apr 29 '21 at 13:43
  • How can I pass an imageView via Safe Args from one fragment to another? – FrensisXh Sep 02 '22 at 09:45
  • what if i'd like to pass new bundles to the current fragment from Activity? I know we can do something like below, but is there another way to not pop back stack and re-navitate? thanks! { val navController = findNavController(R.id.nav_host_fragment) val id = navController.currentDestination?.id navController.popBackStack(id!!, true) navController.navigate(id, bundle) } – Huigege Sep 16 '22 at 17:11
14

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

Vivek Hande
  • 929
  • 9
  • 11
9

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

Mayokun
  • 1,084
  • 1
  • 8
  • 20
3

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

  1. In Navigation file, res/navigation/nav_graph.xml
  2. Declare argument tag in any fragment or inner fragment with action tag
  3. 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

Pratik Dodiya
  • 2,337
  • 1
  • 19
  • 12
2

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>
bromden
  • 284
  • 3
  • 12
1

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!

Dievskiy
  • 199
  • 3
  • 4
1

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()

loonighan
  • 431
  • 6
  • 9
1

For Fragments Uri(String) example(Kotlin):

First Fragment:

            val args = Bundle()
            args.putString("uri", imageuri)
            findNavController()
            .navigate(R.id.action_FirstFragment_to_SecondFragment, args)

Second Fragment:

        val uri = arguments?.getString("uri")
        Toast.makeText(requireContext(), "URI: $uri", Toast.LENGTH_SHORT).show()
Maher
  • 342
  • 3
  • 10
0

Fragment 1

 Bundle bundle=new Bundle();
  bundle.putInt("id",1);
  Fragment fragment=Fragment1.this; //current fragment name
  fragment.setArguments(bundle);
  NavHostFragment.findNavController(fragment)
  .navigate(R.id.action_FirstFragment_to_SecondFragment,bundle);
  //1st arug is navigation action app:destination name defined in nav.graph 

Fragment 2

public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    binding = SecondFragmentBinding.inflate(inflater, container, false);
    Bundle bundle=getArguments();
    Integer id=bundle.getInt("id");
    Toast.makeText(requireContext() , id, Toast.LENGTH_SHORT).show();});
Ehsäɳ Khʌɳ
  • 111
  • 1
  • 3
-2

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.

tuti
  • 1
  • 1