Navigation Component

Android Development Fundamentals
Course 1 Β· Chapter 7 Β· Navigation Component

🧭 Navigation Component

Last chapter's FragmentManager transactions work, but every "go here," back-stack entry, and passed argument was written by hand. The Navigation Component replaces that with a declarative nav graph: destinations and the paths between them are described in one XML resource, and the library handles transactions, the back stack, and (via SafeArgs) type-safe argument passing automatically.

πŸ—ΊοΈ The Nav Graph

A nav graph is an XML resource listing every destination (usually a Fragment) and the actions connecting them:

// 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/welcomeFragment"> <fragment android:id="@+id/welcomeFragment" android:name=".WelcomeFragment" android:label="Welcome"> <action android:id="@+id/action_welcome_to_details" app:destination="@id/detailsFragment" /> </fragment> <fragment android:id="@+id/detailsFragment" android:name=".DetailsFragment" android:label="Details" /> </navigation>

Android Studio's Navigation Editor shows this graph visually, as boxes connected by arrows β€” dragging a connection between two Fragments writes the corresponding <action> XML automatically, which is normally faster than hand-editing this file directly.

🏠 NavHostFragment β€” Where the Graph Lives

An Activity hosts the whole graph through a single special Fragment, replacing the plain FrameLayout container from Chapter 6:

// activity_main.xml <androidx.fragment.app.FragmentContainerView android:id="@+id/navHostFragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:navGraph="@navigation/nav_graph" app:defaultNavHost="true" />

app:navGraph wires this container to the nav graph resource; app:defaultNavHost="true" makes the system Back button correctly step backward through the nav graph's own back stack, rather than falling through to the Activity's default Back behavior.

➑️ Navigating Between Destinations

// Inside WelcomeFragment, e.g. a button's click listener binding.viewDetailsButton.setOnClickListener { findNavController().navigate(R.id.action_welcome_to_details) }

findNavController() locates the NavController managing the graph this Fragment lives in, and navigate(...) follows the specified action β€” no manual FragmentManager.beginTransaction() call needed anymore. The library also automatically pushes this navigation onto a back stack, so the system Back button (and an app's own explicit "up" button) work correctly without any extra code.

πŸ”’ SafeArgs β€” Type-Safe Argument Passing

Chapter 4's Intent.putExtra / getStringExtra pattern is stringly-typed and easy to typo. SafeArgs (a Gradle plugin, added to build.gradle.kts) generates typed argument classes directly from arguments declared in the nav graph:

// nav_graph.xml β€” declaring an argument on a destination <fragment android:id="@+id/detailsFragment" android:name=".DetailsFragment"> <argument android:name="userName" app:argType="string" /> </fragment>
// Sending β€” a generated Directions class, not a raw Bundle val action = WelcomeFragmentDirections.actionWelcomeToDetails(userName = "Philip") findNavController().navigate(action)
// Receiving β€” a generated Args class, in DetailsFragment private val args: DetailsFragmentArgs by navArgs() // property delegation, from Kotlin Intermediate Ch4 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.detailText.text = args.userName // typed String, no getStringExtra, no key string, no null check }

Intent Extras vs SafeArgs

Intent.putExtra (Chapter 4)SafeArgs
KeyRaw string, must match exactlyNamed parameter β€” checked by the compiler
TypeChosen getter (getStringExtra, etc.) β€” can mismatchDeclared once in the nav graph, generated correctly everywhere
Missing valueRuntime null / crashCompile error if a required argument isn't provided

args: DetailsFragmentArgs by navArgs() reuses Kotlin Intermediate Chapter 4's property delegation directly β€” navArgs() is itself a delegate provided by the Navigation library, following the exact same by mechanism as lazy or a custom delegate.

πŸ”™ Back Stack Management

Every navigate() call pushes the new destination onto the graph's back stack automatically. An action can also specify popUpTo to remove destinations from the stack as part of navigating β€” useful for flows like "after login, don't let Back return to the login screen":

<action android:id="@+id/action_login_to_home" app:destination="@id/homeFragment" app:popUpTo="@id/loginFragment" app:popUpToInclusive="true" />

popUpTo="@id/loginFragment" with popUpToInclusive="true" removes loginFragment itself (and anything above it) from the back stack while navigating to homeFragment β€” pressing Back from the home screen then skips login entirely, exiting the app instead, exactly the behavior a real login flow needs.

πŸ”— Deep Links

A deep link lets an external source (a notification, a web link, another app) open the app directly at a specific destination, rather than always starting from the launcher Activity:

<fragment android:id="@+id/detailsFragment" android:name=".DetailsFragment"> <deepLink app:uri="myapp://details/{userName}" /> </fragment>

Tapping a myapp://details/Philip link anywhere on the device (a notification, a browser link with the app's scheme registered) launches the app directly into DetailsFragment, with userName automatically populated as a SafeArgs argument β€” no manual Intent parsing required.

Navigation Component vs Web Routing

React Router / Next.jsNavigation Component
Route definitions<Route path="/details/:id"> or file-based routesnav_graph.xml destinations + actions
Typed paramsuseParams() (often untyped without extra work)SafeArgs-generated Args classes
Programmatic navigationnavigate("/details/5")findNavController().navigate(action)
Deep linkingJust... a URL, inherentlyExplicit <deepLink> declarations needed

πŸ’» Coding Challenges

Challenge 1: Convert Chapter 6's Fragments to a Nav Graph

Take WelcomeFragment and DetailsFragment from Chapter 6, create a nav_graph.xml with both as destinations and an action between them, replace the FrameLayout container with a NavHostFragment, and replace the manual FragmentManager.replace() call with findNavController().navigate().

Goal: Directly experience the FragmentManager-to-NavGraph migration.

β†’ Solution

Challenge 2: Pass an Argument with SafeArgs

Add the SafeArgs Gradle plugin, declare a String argument named "message" on detailsFragment in the nav graph, send it from WelcomeFragment using the generated Directions class, and display it in DetailsFragment using navArgs().

Goal: Practice the full SafeArgs round trip, replacing the Intent-extras pattern from Chapter 4 with its type-safe nav-graph equivalent.

β†’ Solution

Challenge 3: popUpTo Behavior

Add a third destination, homeFragment, and an action from detailsFragment to homeFragment that uses popUpTo/popUpToInclusive to clear both welcomeFragment and detailsFragment off the back stack. In a comment, describe what pressing Back from homeFragment would do as a result.

Goal: Practice configuring back stack behavior declaratively via an action's attributes.

β†’ Solution

πŸ’‘ Course 1 Nearly Complete β€” One Chapter Left

Navigation Component is genuinely how most real, multi-screen Android apps handle moving between screens today β€” the manual FragmentManager approach from Chapter 6 is worth understanding as the foundation, but this declarative graph-based approach is what actual production code reaches for. The final chapter covers resources and styling in more depth, rounding out Course 1's foundation.

🎯 What's Next

Next chapter β€” the final chapter of Course 1: Resources & Styling β€” string/color/dimen resources in depth, themes, and dark mode basics.