Navigation in Android App Using Navigation Component

In this tutorial, you can learn about navigation component and how to implement navigation using navigation component with an example.

Table of Contents

How Navigation Component Works

A task or a feature in an app usually consists of several screens where data is viewed, selected or entered. In android app each screen is implemented as a Fragment or an Activity.

When user starts a task, screens or destination in the flow to complete the task are shown one after another. Usually a button click or an item selection handler starts next fragment or activity (destination) in the flow.

With Navigation component framework, all the destinations of a flow are defined in the xml, which includes the definition for linking of destinations. All that needs to be done in the code to show the next screen in the flow in response to an event on a UI widget of current screen is to use Navigation component class and passing next screen destination ID defined in the xml to it.

The xml which contains navigation info needs to be created in navigation resource folder. To define destinations and links in xml, you can directly add corresponding xml elements to it in text view or it can be created using the navigation editor which can be accessed by opening the navigation xml file and clicking the design tab.

Following navigation graph xml contains two fragment destinations. Starting destination can be defined by assigning a destination id to startDestination attribute of navigation root element. In the below example searchFlights destination is the starting destination. The destination searchFlights is linked to flightsList destination by adding action element to searchFlights destination and assigning flightsList destination id to destination attribute of the action element.

<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" app:startDestination="@id/searchFlights"> <fragment android:id="@+id/searchFlight" android:name="com.zoftino.flights.SearchFlightsFragment" android:label="search_flights" tools:layout="@layout/search_flight" > <action android:id="@+id/action_search_to_list" app:destination="@id/flightsList" /> </fragment> <fragment android:id="@+id/flightsList" android:name="com.zoftino.flights.FlightsListFragment" android:label="flights_list_fragment" tools:layout="@layout/flights_list_fragment" /> </navigation>

After navigation xml is defined, navigation definition needs to be added to the activity which manages the flow of the task. This is done by defining a fragment in the activity layout using NavHostFragment class provided by navigation framework as fragment and adding the navigation graph to it by setting navGraph attribute to point to resource id of navigation xml.

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="zoftino.com.firestore.FlightActivity"> <fragment android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/flight_nav_fragment" android:name="androidx.navigation.fragment.NavHostFragment" app:navGraph="@navigation/flight_nav_graph" app:defaultNavHost="true"/> </LinearLayout>

After the navigation xml is tied to an activity, destinations defined in the xml needs to be linked to UI widgets starting from the start destination so that next screens are shown in response to the events of the widgets on previous pages. This can be done using NavController class. You need to call navigate method on NavController by passing the destination id in the widget event handler, for example button click handler.

searchFlightsButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Navigation.findNavController(view).navigate(R.id.action_search_to_list); } });

That is how destinations are defined and connected to enable navigation in android apps using navigation component.

Setup

You need to add following navigation component dependencies to your module’s build.gradle file.

implementation 'android.arch.navigation:navigation-fragment:1.0.0-alpha05' implementation 'android.arch.navigation:navigation-ui:1.0.0-alpha05'

To use navigation component editor, which helps you in creating navigation graph, you need to install android studio 3.2 or later versions and enable navigation editor by going to settings, experimental and checking Enable Navigation Editor option.

Navigation Component Example

Following example shows how to use navigation component. It uses shopping and checkout tasks and defines two navigation graphs for each task. Navigation graph contains fragment and activity destinations. Shopping flow contains products list fragment destination and product details fragment destination. Checkout flow contains address fragment and payment activity.

Menu options to access these two tasks are provided as options menu on app bar.

Navigation graph (shopping_nav_graph)

Here is the navigation graph for shopping flow.

<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" app:startDestination="@id/productListDestination"> <fragment android:id="@+id/productListDestination" android:name="com.zoftino.navigation.ProductsListFragment" android:label="product_list" tools:layout="@layout/products_list_layout"> <action android:id="@+id/action_list_details" app:destination="@id/productDetailsDestination"/> </fragment> <fragment android:id="@+id/productDetailsDestination" android:name="com.zoftino.navigation.ProductDetailsFragment" android:label="product_details" tools:layout="@layout/product_details" /> </navigation>

ProductsListFragment

public class ProductsListFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.products_list_layout, container, false); ((Button)view.findViewById(R.id.product_details_b)) .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Navigation.findNavController(view).navigate(R.id.action_list_details); } }); return view; } }

public class ProductDetailsFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.product_details, container, false); return view; } }

Navigation graph (checkout_nav_graph)

<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" app:startDestination="@id/addressDestination"> <fragment android:id="@+id/addressDestination" android:name="com.zoftino.navigation.AddressFragment" android:label="address" tools:layout="@layout/address"> <action android:id="@+id/action_address_payment" app:destination="@id/paymentDestination"/> </fragment> <activity android:id="@+id/paymentDestination" android:name="com.zoftino.navigation.PaymentActivity" android:label="payment" tools:layout="@layout/payment_activity" /> </navigation>

AddressFragment

public class AddressFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.address, container, false); ((Button)view.findViewById(R.id.payment_b)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Navigation.findNavController(view).navigate(R.id.action_address_payment); } }); return view; } }

PaymentActivity

public class PaymentActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.payment_activity); } }

Main activity

The main activity inflates options menu which provides options for user to start either shopping flow or checkout flow. Launcher activity layout contains frame layout which works as a place holder for fragment destinations of the selected flow. The fragment (NavHostFragment) defined in the activity layout points to shopping flow navigation graph, so the starting screen will be product list fragment.

public class ShoppingActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.shopping_activity); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.shop_menu, menu); return true; } public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.products: switchNavGraph(R.navigation.shopping_nav_graph); return true; case R.id.checkout: switchNavGraph(R.navigation.checkout_nav_graph); return true; default: return super.onOptionsItemSelected(item); } } private void switchNavGraph(int navResourceId){ NavHostFragment finalHost = NavHostFragment.create(navResourceId); getSupportFragmentManager().beginTransaction() .replace(R.id.content_frame, finalHost) .setPrimaryNavigationFragment(finalHost) .commit(); } }

Main activity layout(shopping_activity)

<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout 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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <FrameLayout android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/shopping_nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" app:navGraph="@navigation/shopping_nav_graph" app:defaultNavHost="true"/> </FrameLayout> </android.support.constraint.ConstraintLayout>

Menu (shop_menu)

<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/products" android:title="Shop" app:showAsAction="never"/> <item android:id="@+id/checkout" android:title="Checkout" app:showAsAction="never"/> </menu>

Navigation Component BottomNavigationView Example

Destinations defined in the navigation graph can be tied to menu items of bottom navigation view. To do that, menu item ids should match to the ids of destinations defined in the navigation graph. In the code, you need to use NavigationUI class to integrate menu items and destinations.

NavigationUI.setupWithNavController(bottomNavigationView, navHostFragment.getNavController());

Activity

public class ShoppingBottomNavActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.shopping_bottom_nav_activity); BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_nav); NavHostFragment navHostFragment = (NavHostFragment)getSupportFragmentManager() .findFragmentById(R.id.shop_nav_host_fragment); NavigationUI.setupWithNavController(bottomNavigationView, navHostFragment.getNavController()); } }

Activity Layout

<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout 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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ShoppingBottomNavActivity"> <fragment android:id="@+id/shop_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/shop_checkout_nav_graph" /> <android.support.design.widget.BottomNavigationView android:id="@+id/bottom_nav" android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:menu="@menu/shop_bottom_nav_menu" /> </android.support.constraint.ConstraintLayout>

Menu

Let’s see how to integrate BottomNavigationView and navigation component with an example. In this example, shopping and checkout flow destinations are defined in one navigation graph and two menu items defined for bottom navigation view connect to start destination of each flow.

Ids of the menu items are same as ids of destinations. Menu item shop points to starting destination of shopping flow and checkout menu item points to starting destination of checkout flow.

<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/productListDestination" android:title="Shop" android:icon="@drawable/shop"/> <item android:id="@+id/addressDestination" android:title="Checkout" android:icon="@drawable/checkout"/> </menu>

Navigation Component Navigation Drawer Example

Like bottom navigation view is used with navigation component, navigation component can be integrated with navigation drawer in the same way.

Activity

public class ShoppingNavigationDrawerActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.shopping_nav_drawer_activity); NavigationView navigationView = findViewById(R.id.navigationView); NavHostFragment navHostFragment = (NavHostFragment)getSupportFragmentManager() .findFragmentById(R.id.shop_nav_host_fragment); NavigationUI.setupWithNavController(navigationView, navHostFragment.getNavController()); } }

Layout

<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout 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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ShoppingNavigationDrawerActivity"> <android.support.v4.widget.DrawerLayout android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/shop_nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" app:navGraph="@navigation/shop_checkout_nav_graph" app:defaultNavHost="true"/> <android.support.design.widget.NavigationView android:id="@+id/navigationView" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" app:menu="@menu/shop_bottom_nav_menu"/> </android.support.v4.widget.DrawerLayout> </android.support.constraint.ConstraintLayout>

Passing Data between Destinations

Data can be passed between destinations. For that, in navigation graph, you need to define arguments in the definition of the fragment which receives the data from source destination.

In the following example, MovieDetailsFragment can receive arugment.

<fragment android:id="@+id/movieDetailsDestination" android:name="com.zoftino.navigation.MovieDetailsFragment" android:label="movie details" tools:layout="@layout/movie_details"> <argument android:name="movieId" android:defaultValue="ab12" /> </fragment>

In the source fragment, where navigation to the next destination happens, you need to create a bundle and add the argument to it and pass it to navigate method of NavController.

Bundle bundle = new Bundle(); bundle.putString("movieId", "DBS183"); Navigation.findNavController(view).navigate(R.id.action_movies_details, bundle);

In the destination fragment, get the data sent from the source fragment.

details_tv.setText("Movie details of "+getArguments().getString("movieId"));

Transitions between Destinations

<fragment android:id="@+id/moviesDestination" android:name="com.zoftino.navigation.MoviesListFragment" android:label="movies" tools:layout="@layout/movie_list"> <action android:id="@+id/action_movies_details" app:destination="@id/movieDetailsDestination" app:enterAnim="@anim/action_enter" app:exitAnim="@anim/action_exit"/> </fragment>

Navigation Component Examples

You can apply animations to the transitions between destinations. You can add animation to the action in the nav graph using enterAnim and exitAnim attributes of action element.

You can find complete code here