NavController: The Single Source of Truth for App Navigation

NavController is the central API for managing screen transitions. Think of it as an air traffic controller for your UI, directing users between destinations. It's the core of Jetpack Navigation, handling clicks that lead to new screens.
WHY IT EXISTS: Before the Jetpack Navigation component, developers managed screen transitions manually using FragmentTransactions or by starting new Activities. This approach was error-prone, difficult to manage, and made tasks like passing data and maintaining a consistent back stack complex. NavController was created to provide a robust, declarative API to centralize and simplify all app navigation logic.
THE MENTAL MODEL: Think of NavController as the captain of a ship, navigating based on a pre-drawn map (your NavGraph). You give it high-level commands like "navigate to the profile screen," and it handles all the low-level mechanics of swapping out the current screen (a Fragment or Composable) and correctly updating its internal back stack. It is the single, authoritative point of contact for all navigation operations, ensuring a predictable user experience.
HOW IT WORKS: A NavController is associated with a single NavHost. The NavHost is an empty container that displays destinations from your navigation graph. The NavGraph, typically defined in an XML resource or with a Kotlin DSL, maps out all your app's screens (destinations) and the paths (actions) between them. When you call a function like navController.navigate("destination_route"), the NavController finds that destination in the graph, performs the transaction to display it in the NavHost, and pushes the previous location onto its back stack. It also automatically handles system "back" presses and "up" button navigation by popping destinations off the stack.
WHEN TO USE IT: NavController is the standard for almost any modern Android app with more than one screen. It is the foundation of the Jetpack Navigation component and is ideal for single-activity architectures using either Fragments or Jetpack Compose. Use it to simplify argument passing between screens, creating animated transitions, and implementing deep links into your app.
WHEN NOT TO USE IT: For an extremely simple, single-screen application, the setup for the Navigation component might be unnecessary overhead. Additionally, if you have a highly dynamic, non-linear navigation flow that cannot be easily represented in a static graph structure, you might need a more custom solution, though this is a very rare edge case. Most complex flows can still be modeled effectively.
ONE CANONICAL EXAMPLE: In a Jetpack Compose app, you first obtain a NavController instance using val navController = rememberNavController(). You then associate it with a NavHost. To navigate from a list screen to a detail screen when a user taps an item, you would invoke navController.navigate("detail_screen/${itemId}"). The NavController uses this route to find the correct Composable destination and displays it, pushing the detail screen onto the navigation stack.
Read the original → developer.android.com
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.