Modern development for Android is unthinkable without using Navigation Component. This powerful tool from the Android Jetpack ecosystem has completely changed the way the user moves between app screens. Previously, developers had to manually keep track of the fragment stack, handle the back button, and pass arguments through Bundlewhich often led to confusing code and subtle errors.

Now the entire navigation process is visualized in a special graph editor, where you can see the structure of your application at a glance. We'll look at how to create a robust Single Activity architecture, enable a bottom menu, and avoid common pitfalls when transferring data. This will make your application stable and easy to maintain.

Fundamentals of Single Activity Architecture

The main principle of modern navigation is based on the concept Single Activity. Instead of creating multiple Activity s for each screen, you use one main window, within which dynamically replace Fragments. This approach greatly simplifies state management and transitions between screens, since all the context remains in one place.

To implement this scheme, you will need to add the necessary dependencies to the build.gradle (module level) file. The Navigation library automatically handles fragment transactions, transition animations, and deep links. This frees you from the chore of writing code for each navigation, allowing you to focus on the logic of the application.

It is important to understand that the navigation graph is the only source of truth about which screens are currently available to the user. It strictly controls access and sequence of actions. If you try to go to a screen that is not in the graph, the system simply will not allow this, which increases the safety and predictability of the interface.

โ˜‘๏ธ Preparing the project for navigation

Done: 0 / 4

Creating and setting up a navigation graph

The central element of the entire system is the navigation graph file, usually called navigation.xml. It is stored in a folder res/navigation and is an XML markup that describes all possible routes in the application. You can create it manually or use the visual editor in Android Studioby dragging elements with the mouse.

Each screen in the graph is represented by a node (destination), which is most often a fragment. Connections between screens are indicated by arrows - actions. These actions determine exactly where the user will be taken when clicking the button and what parameters will be passed. Visual representation helps you immediately see logical breaks or cyclic dependencies that might otherwise escape attention when writing code.

โš ๏ธ Warning: Do not create actions in both directions between two fragments unless necessary. This may result in duplicate entries in the navigation stack and the system return button not working correctly. Always think through the transition hierarchy in advance.

For complex applications, the graph can be divided into several nested graphs. This allows you to modularize navigation by hiding the internal details of one function block from another. For example, the authorization flow can be a separate subgraph, which is replaced by the main application graph after a successful user login.

What is Deep Link in navigation?

Deep Link allows you to open a specific application screen directly from an external source (browser, notification), bypassing the standard sequence of transitions. In the graph, this is configured through the app:deepLink attribute.

Integration of NavHostFragment into the layout

For the navigation graph to โ€œcome to lifeโ€, it must be linked to the user interface. To do this, a special container is added to the XML layout of your only one Activity . It is he who acts as the master for all other fragments and manages their life cycle within the navigation. NavHostFragment. It is he who acts as the master for all other fragments and manages their life cycle within the framework of navigation.

In the markup code, you must indicate the ID of your navigation graph through the attribute app:navGraph. It is also important to set the start destination, which will load first when you launch the application. Without this step, the application will not know where to start displaying content.

<androidx.fragment.app.FragmentContainerView

android:id="@+id/nav_host_fragment"

android:name="androidx.navigation.fragment.NavHostFragment"

app:navGraph="@navigation/nav_graph"

app:defaultNavHost="true"

... />

The attribute app:defaultNavHost="true" plays a critical role. It links the physical back button on the device to the logic of the navigation component. If this is not done, clicking the button will close the entire application instead of returning to the previous screen within the fragment stack. This is one of the most common mistakes made by beginners when setting up for the first time.

Managing transitions via NavController

The logical center for controlling movements is the object NavController. It cannot be created manually via the constructor; instead, you access it through NavHostFragment or while inside the fragment using an extension findNavController(). This controller knows the current state of the stack and executes all transition commands.

The navigate()method is used to perform a transition. You can pass it the ID of the action defined in the graph, or the ID of the target fragment directly. Using an action ID is preferable because it ensures that the transition exists in the graph and that the data transfer parameters are correct. Direct navigation by fragment ID is possible, but less secure in terms of argument typing.

When working with NavController it is important to consider the lifecycle state. Attempting to navigate when the fragment has already been destroyed or is not attached to the activity will crash the application. Always check for a valid controller before calling navigation methods, especially in asynchronous operations or callbacks.

๐Ÿ“Š Which navigation method do you use most often?
Multiple Activities
Single Activity + Fragments
Compose Navigation
Custom Router
Do not use navigation

Passing data between screens

One of the main tasks of navigation is to transfer data from one screen to another. In older implementations, this was done through static fields or Intent, which was unsafe. Navigation Component offers a type-safe way to pass arguments via Safe Args.

The Safe Args plugin generates classes based on your XML graph. For each fragment and action, corresponding classes are created that strictly type the passed parameters. You can no longer accidentally pass a string where a number is expected, as the code simply won't compile. This eliminates a whole class of runtime errors (Runtime Exceptions).

Data type Support Transmission features
Primitives (int, boolean) Full Transmitted directly without serialization
String Full Automatic processing of null
Parcelable Full Requires implementation of the interface in the class
Serializable Limited Not recommended due to performance
Reference No You cannot pass object references directly

To use this function, you must define the arguments in the graph XML file inside the tag <argument>. After building the project, you will be able to access the generated Directions and Args classes. This makes the code clean, understandable, and safe from typos in argument keys.

๐Ÿ’ก

Use Safe Args classes even to pass simple flags. This disciplines the code and makes it easy to expand data transfer functionality in the future without rewriting the logic.

Bottom navigation and top bar

Most applications use standard controls to facilitate user orientation. BottomNavigationView (bottom menu) and Toolbar (top bar) are easy integrate with Navigation Component. You don't need to write click handlers for each menu item manually.

Just call the method setupWithNavController()passing an instance of the navigation controller to it. After this, the menu will automatically highlight the active item when switching between tabs. If the user clicks on an already active item, the application can return him to the main screen of that tab, which is a good form of UX.

The top panel (Action Bar) can also automatically display the Back button and the title of the current screen. To do this, you need to configure AppBarConfiguration, indicating which screens are top-level destinations. On these screens, the "Back" button is hidden, and on nested ones it appears automatically.

โš ๏ธ Attention: Interfaces and support methods for BottomNavigationView may change in new versions of Material Design libraries. If you are using the newer version of Material3 (Material You), check the documentation as some setupWithNavController methods may have been replaced or require different dependencies.

Don't forget to handle the case when the user is on the top-level screen and presses the system "Back" button. By default the application will close. Often you need to change this behavior to prompt the user to confirm exit or minimize the application to the background, rather than ending the process completely.

๐Ÿ’ก

Automatic synchronization of the menu and panel title through setupWithNavController saves dozens of lines of code and ensures visual consistency of the interface.

Common errors and their solutions

Even when in use With powerful tools, developers face challenges. One of the most common is the loss of fragment state when rotating the screen or changing the configuration. The Navigation Component tries to preserve the stack, but if you use the wrong fragment constructors or don't store data in the ViewModel, information can be lost.

Another problem is navigation leakage. This happens when you call navigate() from a context that is no longer active, or create new instances NavController where you need to use an existing one. Always try to get the controller through the view owner (View) or fragment to ensure binding to the correct lifecycle.

Also worth mentioning is the problem with deep links (Deep Links). If an app is opened from a link, but the navigation graph is not configured correctly, the user may end up with a blank screen or see an incorrect navigation stack. Testing external login scripts should be a mandatory part of the development process.

Is it possible to use a Navigation Component with multiple Activities?

Technically yes, but this is contrary to the recommended Single Activity architecture. To switch between Activities, it is better to use regular Intents, and within each Activity use its own independent navigation graph. Mixing approaches complicates support.

How to animate transitions between fragments?

Animations are set directly in the XML navigation graph in the action attributes. You can specify enter, exit, popEnter and popExit animations using standard Android resources or your own animators.

What if Safe Args does not generate classes?

Check if the plugin is included androidx.navigation.safeargs.kotlin in the project and module's build.gradle file. Also try running the Clean Project and Rebuild Project commands to trigger code generation.

How to clear the navigation stack to a specific screen?

Use an action graph (navGraph) and the navigate method with popUpTo flags. This allows you to remove all intermediate screens from the stack and leave only the target screen, which is useful after a successful login or completion of an order.