24

I've got the following Activity, which is the single one in my app:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        PermissionState state = PermissionState.get(this);
        if (state == PermissionState.GRANTED) {
            getWindow().setBackgroundDrawable(new ColorDrawable(-1));
        }

        super.onCreate(savedInstanceState);

        ActivityMainBinding mainActivityBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        setSupportActionBar(mainActivityBinding.toolbarMain);
        NavigationUI.setupWithNavController(mainActivityBinding.toolbarMain, Navigation.findNavController(this, R.id.nav_host_fragment));
    }
}

The layout associated with the Activity is the following and was created following this Android Developers tutorial.

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay">

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:popupTheme="@style/AppTheme.PopupOverlay" />

        </com.google.android.material.appbar.AppBarLayout>

        <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:navGraph="@navigation/nav_graph" />

    </LinearLayout>

</layout>

The navigation graph is the following:

nav_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/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.example.myapp.MainFragment"
        android:label="@string/app_name"
        tools:layout="@layout/fragment_main">
        <action
            android:id="@+id/action_mainFragment_to_settingsFragment"
            app:destination="@+id/settingsFragment" />
    </fragment>

    <fragment
        android:id="@+id/settingsFragment"
        android:name="com.example.myapp.SettingsFragment"
        android:label="@string/settings" />
</navigation>

Now, other than the hosting Activity, my app has two Fragments, one is the main, the other is for settings.
The main Fragment is the start destination of my app, and there I would like to retrieve the MainActivity variable "PermissionState state" to perform some actions based on the state of the permissions, like hiding or showing some Views.

Using Android Architecture Component Navigation API version 1.0.0-beta01 onwards, what is the correct, nondeprecated strategy to pass data from the hosting Activity to the start destination Fragment, maybe using Safe Args if possible?

Oliver
  • 926
  • 2
  • 12
  • 31

2 Answers2

36

Find a solution,works fine but i don't like it there's must be a better solution for that.

In your Activity:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val navController = findNavController(R.id.nav_controller_fragment)
    val bundle = Bundle()
    bundle.putString("name","your value")
    navController.setGraph(navController.graph,bundle)
}

In your start Fragment:

  val args: MainFragmentArgs by navArgs()
  textView.text = args.name

In your navGraph:

<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/mainFragment">

<fragment android:id="@+id/mainFragment"
          android:name="com.haidar.mediasaver.fragments.MainFragment"
          android:label="main_fragment"
          tools:layout="@layout/main_fragment">
    <argument android:name="name"
              app:argType="string"/>
</fragment>

Hussnain Haidar
  • 2,200
  • 19
  • 30
  • Thanks for the reply! Why don't you like this solution? What are the drawbacks? – Oliver Feb 10 '19 at 13:56
  • There is no drawback. I just don't want to set graph twice. I find a method navController.getGraph.add arguments() and I think that's maybe the better way. But I didn't know much about this method working. – Hussnain Haidar Feb 10 '19 at 14:01
  • 1
    Why `Bundle` instead of SafeArgs? – IgorGanapolsky Feb 02 '20 at 23:35
  • With last version of Navigation component, use MainFragmentArgs.Builder to create start fragment bundle. – A. Ferrand Feb 06 '20 at 11:47
  • ^ @A.Ferrand How? – Zeeshan Shabbir Nov 03 '20 at 21:49
  • is there any way I can save that arg, so that when I switch bottom nav tabs it is not null, I actually have bottom navigation set using navigation component, when I pass data from activity to first fragment of bottom nav it works, but when I switch my bottom navigation tabs it comes as null. – Rahul Pawar Nov 25 '20 at 07:56
  • setContentView(R.layout.activity_main) makes start destination to be created with default arguments. When you call navController.setGraph(navController.graph,bundle) start destination is called again with real argument values. So problem is that default destination fragment will be called two times. I haven't found example code to prevent this behavior. Google does not provide working example in official documentation https://developer.android.com/guide/navigation/navigation-pass-data#start – Reijo Korhonen May 24 '23 at 19:37
22

If you use safe args, you need to do:

  1. Remove the app:navGraph attribute from your NavHostFragment
  2. Then call:

    findNavController(R.id.nav_controller_fragment)
                     .setGraph(
                          R.navigation.nav_graph,
                          MainFragmentArgs("your values").toBundle()
    )
    
cherif
  • 1,164
  • 1
  • 11
  • 16