Navigation Component (Java)

The Navigation Component replaces manual FragmentTransaction code with a declarative nav graph, a NavHostFragment container, and a NavController that handles back stack, transitions, and Up behavior. With Safe Args you also get type-safe argument passing in Java.

Why Use It?

Declarative Graph

All destinations & actions in one XML

Back/Up handled

Correct behavior out-of-the-box

Safe Args

Type-safe arguments in Java

Deep Links

Open screens from URLs/notifications

NavHostFragment Hosts fragments NavController Navigates with actions nav_graph.xml Destinations & args

Setup (Gradle)

// Project build.gradle (settings may vary)
buildscript {
  dependencies {
    classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.7.7'
  }
}

// Module: app/build.gradle
plugins {
  id 'com.android.application'
  id 'androidx.navigation.safeargs'     // Java Safe Args
}
dependencies {
  implementation 'androidx.navigation:navigation-fragment:2.7.7'
  implementation 'androidx.navigation:navigation-ui:2.7.7'
  implementation 'com.google.android.material:material:1.12.0'
}
Explanation: The Safe Args plugin generates Directions and Args classes for type-safe navigation and argument reading in Java.

Create the Nav Host

Add a NavHostFragment container (usually in your Activity layout). It swaps in/out destination fragments.

<!-- res/layout/activity_main.xml -->
<androidx.fragment.app.FragmentContainerView
    android:id="@+id/nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:navGraph="@navigation/nav_graph"
    app:defaultNavHost="true" />
Explanation: app:navGraph links the graph. defaultNavHost="true" means the host intercepts system back button to pop the nav back stack.

Build the Navigation Graph

<!-- res/navigation/nav_graph.xml -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/nav_graph"
            app:startDestination="@id/firstFragment">

  <fragment
      android:id="@+id/firstFragment"
      android:name="com.example.FirstFragment"
      android:label="First">

    <!-- Type-safe argument for destination -->
    <argument android:name="userName"
               app:argType="string"
               android:defaultValue="Guest"/>

    <!-- Action to navigate to SecondFragment -->
    <action
        android:id="@+id/action_first_to_second"
        app:destination="@id/secondFragment"
        app:popUpTo="@id/firstFragment"
        app:popUpToInclusive="false" />

  </fragment>

  <fragment
      android:id="@+id/secondFragment"
      android:name="com.example.SecondFragment"
      android:label="Second" />

</navigation>
Explanation: The graph defines two destinations. The action action_first_to_second is a named path from First → Second. The argument becomes a generated getter in FirstFragmentArgs/SecondFragmentArgs or a param on the generated Directions method when navigating.

MainActivity: App Bar + Up Button

// MainActivity.java
public class MainActivity extends AppCompatActivity {
  private AppBarConfiguration appBarConfiguration;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Toolbar toolbar = findViewById(R.id.toolbar); // if using a Toolbar
    setSupportActionBar(toolbar);

    NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
    appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
    NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
  }

  @Override
  public boolean onSupportNavigateUp() {
    NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
    return NavigationUI.navigateUp(navController, appBarConfiguration) || super.onSupportNavigateUp();
  }
}
Explanation: setupActionBarWithNavController syncs the title and shows the Up arrow when needed. navigateUp() delegates correct behavior (drawer vs back stack).

Navigating in Java (with Safe Args)

Inside FirstFragment, create a click listener that navigates to SecondFragment with a typed argument.

// FirstFragment.java
NavController nav = NavHostFragment.findNavController(this);

// Using generated Directions class:
FirstFragmentDirections.ActionFirstToSecond action =
        FirstFragmentDirections.actionFirstToSecond();
action.setUserName("Sonu");
nav.navigate(action);

In SecondFragment, read the argument safely:

// SecondFragment.java
SecondFragmentArgs args = SecondFragmentArgs.fromBundle(getArguments());
String name = args.getUserName(); // "Sonu"
Explanation: The Safe Args plugin generates FirstFragmentDirections (for building actions) and SecondFragmentArgs (for reading args), so you don’t manually parcel or cast.

NavigationUI with BottomNavigationView

Hook your bottom nav to the controller and declare top-level destinations (no Up arrow among them).

<!-- res/layout/activity_main.xml (excerpt) -->
<com.google.android.material.bottomnavigation.BottomNavigationView
    android:id="@+id/bottom_nav"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:menu="@menu/menu_bottom" />
// MainActivity.java (after navController creation)
BottomNavigationView bottom = findViewById(R.id.bottom_nav);
NavigationUI.setupWithNavController(bottom, navController);

appBarConfiguration = new AppBarConfiguration.Builder(
        R.id.homeFragment, R.id.searchFragment, R.id.profileFragment  // top-level IDs
).build();

Deep Links

Open screens via URLs/intent filters. Add a deep link to a destination:

<!-- In nav_graph destination -->
<deepLink app:uri="https://codingwithsonu.com/user/{userId}" />

And in the manifest for NavHostActivity (your activity):

<activity android:name=".MainActivity"
          android:exported="true">
  <nav-graph android:value="@navigation/nav_graph" />
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="https"
          android:host="codingwithsonu.com" />
  </intent-filter>
</activity>

Pop Behavior & Back Stack Control

Property / FlagEffectWhen to Use
popUpToPop stack up to a destinationClear intermediate screens
popUpToInclusiveInclude the target in the popReset to root/home
app:launchSingleTopAvoid multiple copies on topIdempotent navigations

Custom Animations (optional)

<action ...
    app:enterAnim="@anim/slide_in_right"
    app:exitAnim="@anim/slide_out_left"
    app:popEnterAnim="@anim/slide_in_left"
    app:popExitAnim="@anim/slide_out_right" />

Manual Transactions vs Navigation

AspectManual FragmentTransactionNavigation Component
Back stackDev must manageManaged by graph/controller
ArgsBundles (unsafe types)Safe Args (generated classes)
Deep linksManual parsingBuilt-in destinations
Up buttonManualNavigationUI handles
TransitionsManual anim logicPer-action attributes

Boilerplate Reduction

Navigation vs manual transactions

* Conceptual (lower boilerplate with Navigation)

Safety & Correctness

Type safety + Up/back consistency

Dialog Destinations

You can add a <dialog> destination (DialogFragment) in the graph and navigate to it like any other destination.

Troubleshooting

“No NavController set” ➜ Ensure your container is NavHostFragment and you call Navigation.findNavController(activity, R.id.nav_host_fragment) or NavHostFragment.findNavController(fragment).

Args class not found ➜ Rebuild project; confirm Safe Args plugin applied and res/navigation/ graph exists.

Up button weird ➜ Make destinations top-level in AppBarConfiguration or remove incorrect popUpTo rules.

Multiple copies on back stack ➜ Use app:launchSingleTop="true" on the action.

Hands-On Exercise

  1. Create FirstFragment and SecondFragment with an action and an argument (userName).
  2. Navigate via a button in First; show the name in Second using SecondFragmentArgs.
  3. Add a third fragment and use popUpTo to clear intermediate screens.
  4. Attach a BottomNavigationView to 3 top-level fragments via NavigationUI.
NavHostActivity (FragmentContainerView) └─ NavController reads nav_graph.xml ├─ FirstFragment (args: userName) │ └─ action to SecondFragment (animations/launchSingleTop) ├─ SecondFragment (reads Safe Args) └─ ThirdFragment (deep link + popUpTo rules)