Using Navigation Component
1. Setting Up Navigation Graph
Navigation graphs define the navigation structure of your app using XML files. They consist of destinations, actions, and navigation parameters.
| Element | Description | Usage |
|---|---|---|
| navigation | Root element for the navigation graph | <navigation> |
| fragment | Represents a destination in the navigation graph | <fragment> |
| action | Defines navigation between destinations | <action> |
Example: Basic Navigation Graph Setup
<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/homeFragment">
<fragment
android:id="@+id/homeFragment"
android:name="com.example.HomeFragment"
android:label="Home" />
<fragment
android:id="@+id/profileFragment"
android:name="com.example.ProfileFragment"
android:label="Profile" />
<action
android:id="@+id/action_home_to_profile"
app:destination="@id/profileFragment" />
</navigation>
Note: The
startDestination attribute specifies the initial destination when the navigation graph is loaded.
2. Configuring NavHost Fragment
| Property | Values | Description |
|---|---|---|
| android:name | androidx.navigation.fragment.NavHostFragment |
The class name for the NavHost Fragment |
| app:navGraph | @navigation/nav_graph |
Reference to the navigation graph resource file |
| app:defaultNavHost | true |
Enables the fragment to receive system back button events |
Example: NavHost Fragment in XML
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:navGraph="@navigation/nav_graph"
app:defaultNavHost="true" />
3. Navigating Between Destinations
| Destination | Navigation Action | Description |
|---|---|---|
| HomeFragment | navController.navigate(R.id.action_home_to_detail) |
Navigates from HomeFragment to DetailFragment |
| ProfileFragment | navController.navigate(R.id.action_profile_to_settings) |
Opens SettingsFragment from ProfileFragment |
Example: Navigation in Kotlin
class HomeFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_home, container, false)
val button = view.findViewById<Button>(R.id.button)
button.setOnClickListener {
findNavController().navigate(R.id.action_home_to_detail)
}
return view
}
}
Note: Navigation actions are defined in
nav_graph.xml and must be unique.Warning: Avoid hardcoding navigation paths; use named actions for maintainability.
4. Passing Arguments
| Argument Type | Description | Usage | Notes |
|---|---|---|---|
| String | Passes a string value | navArgs.putString("name", "John") |
Can be null |
| Int | Passes an integer value | navArgs.putInt("age", 25) |
Supports all integer types |
| Boolean | Passes a boolean value | navArgs.putBoolean("isActive", true) |
Can be null |
| Serializable | Passes a serializable object | navArgs.putSerializable("user", userObject) |
Object must implement Serializable |
| Parcelable | Passes a parcelable object | navArgs.putParcelable("user", userObject) |
Object must implement Parcelable |
Example: Passing Arguments in Navigation
class UserFragment : Fragment() {
private val args: UserFragmentArgs by navArgs()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_user, container, false)
val userName = args.userName
val userAge = args.userAge
// Use the arguments here
return view
}
}
Example: Passing Arguments in Navigation Graph
<fragment
android:id="@+id/userFragment"
android:name="com.example.UserFragment"
tools:layout="@layout/fragment_user" >
<argument
android:name="userName"
app:argType="string" />
<argument
android:name="userAge"
app:argType="integer" />
</fragment>
Note: Arguments must be defined in the navigation graph before they can be used in a fragment.
5. Using Navigation Actions
| Action | Description | Parameters | Example |
|---|---|---|---|
| navigate() | Navigates to a destination | navController, actionId |
navController.navigate(R.id.action_to_destination) |
| popBackStack() | Removes the top destination from the back stack | navController, inclusive |
navController.popBackStack(true) |
| navigateUp() | Navigates up to the parent destination | navController |
navController.navigateUp() |
Example: Using Navigation Actions in a Fragment
class HomeFragment : Fragment() {
private lateinit var navController: NavController
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_home, container, false)
navController = findNavController()
view.findViewById<Button>(R.id.button).setOnClickListener {
navController.navigate(R.id.action_home_to_details)
}
return view
}
}
Note: Navigation actions are defined in the
nav_graph.xml file and must be unique within a navigation graph.
Warning: Avoid using
navigateUp() in fragments that are not part of a navigation graph.
6. Handling Deep Links
| Property | Description | Example |
|---|---|---|
| deepLink | Defines a deep link URI pattern for navigation | deepLink { uri = "https://example.com/product/{id}" } |
| uri | URI pattern for deep link matching | uri = "app://myapp/product/{id}" |
Example: Handle Deep Link in Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navController = findNavController(R.id.nav_host_fragment)
val navGraph = navController.navInflater.inflate(R.navigation.nav_graph)
// Handle deep link
if (intent.data != null) {
val navOptions = NavOptions.Builder()
.setLaunchSingleTop(true)
.build()
navController.navigate(
R.id.productFragment,
Bundle().apply { putString("id", intent.data?.lastPathSegment) },
navOptions
)
}
}
}
Note: Deep links must be defined in the navigation graph and handled in the activity or fragment that receives them.
7. Implementing Conditional Navigation
| Condition | Description | Implementation |
|---|---|---|
| Navigation Graph | Defines navigation flow and destinations | |
| Safe Args Plugin | Generates type-safe arguments for navigation | |
| Conditional Navigation | Navigation based on runtime conditions | |
Example: Conditional Navigation in Fragment
class HomeFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_home, container, false)
val navController = findNavController()
view.findViewById<Button>(R.id.button).setOnClickListener {
val userLoggedIn = getUserStatus()
if (userLoggedIn) {
navController.navigate(R.id.action_home_to_profile)
} else {
navController.navigate(R.id.action_home_to_login)
}
}
return view
}
}
Note: Use
findNavController() to access the NavController from any Fragment.Warning: Avoid hardcoding navigation logic in UI components; prefer centralized navigation handling.
8. Using Bottom Navigation with NavController
| Property | Description | Values |
|---|---|---|
| navController | Instance of NavController used to navigate between destinations | NavController |
| bottomNavigationView | Reference to the BottomNavigationView component | BottomNavigationView |
Example: Setting up Bottom Navigation with NavController
class MainActivity : AppCompatActivity() {
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
navView.setupWithNavController(navController)
}
}
Note: The
setupWithNavController method automatically maps the navigation graph to the bottom navigation items.
Key Points
NavControllermanages navigation between destinations.BottomNavigationViewprovides a UI for bottom navigation.- Use
setupWithNavControllerto bind them together.
9. Managing Navigation Backstack
Example: Clearing the Back Stack
val navController = findNavController()
navController.navigate(R.id.action_to_destination) {
popUpTo(R.id.nav_graph_start) {
inclusive = true
}
}
| Property | Description | Values |
|---|---|---|
| popUpTo | Destination ID to pop up to | R.id.destination_id |
| inclusive | Whether to include the destination in the pop | true or false |
Note: Use
popUpTo with inclusive = true to clear the back stack up to and including a specific destination.Example: Adding to Back Stack
val navController = findNavController()
navController.navigate(R.id.action_to_destination) {
// Default behavior adds to back stack
}
Workflow: Managing Back Stack
- Identify the destination to navigate to
- Determine if you want to clear or preserve the back stack
- Use
popUpTowith appropriate flags to manage the stack - Ensure proper handling of navigation in UI components
10. Navigating with Animations
| Animation Type | Description | Usage |
|---|---|---|
| slide | Slide animation from left to right or vice versa | navOptions { anim { enter = R.anim.slide_in_right; exit = R.anim.slide_out_left } } |
| fade | Fade in/out animation | navOptions { anim { enter = R.anim.fade_in; exit = R.anim.fade_out } } |
| popUpTo | Pop back to a specific destination | navOptions { popUpTo(R.id.homeFragment) } |
Example: Slide Animation
val navOptions = NavOptions.Builder()
.setEnterAnim(R.anim.slide_in_right)
.setExitAnim(R.anim.slide_out_left)
.build()
navController.navigate(R.id.destination, null, navOptions)
Example: Fade Animation
val navOptions = NavOptions.Builder()
.setEnterAnim(R.anim.fade_in)
.setExitAnim(R.anim.fade_out)
.build()
navController.navigate(R.id.destination, null, navOptions)
11. Using Navigation Compose
Example: Basic Navigation Compose Setup
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
@Composable
fun NavigationExample() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home"
) {
composable("home") {
HomeScreen(navController)
}
composable("details") {
DetailsScreen()
}
}
}
@Composable
fun HomeScreen(navController: NavHostController) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
// Home content
}
}
@Composable
fun DetailsScreen() {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
// Details content
}
}
| Feature | Description | Notes |
|---|---|---|
| NavHost | NavHost is the container for navigation graph |
Must be used inside a Composable |
| composable | Defines a screen in the navigation graph | Used to define each screen in the app |
| startDestination | Initial screen to display | Must be a valid route name |
Note: Navigation Compose is part of Jetpack Compose and requires
androidx.navigation:navigation-compose dependency.Example: Passing Data Between Screens
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.navArgument
import androidx.navigation.compose.rememberNavController
@Composable
fun DataPassingExample() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home"
) {
composable(
"home",
arguments = listOf(
navArgument("userId") {
type = NavType.IntType
}
)
) { backStackEntry ->
val userId = backStackEntry.arguments?.getInt("userId") ?: 0
HomeScreenWithId(userId, navController)
}
composable("details/{userId}") { backStackEntry ->
val userId = backStackEntry.arguments?.getString("userId") ?: ""
DetailsScreenWithId(userId)
}
}
}
Example: Navigation with Deep Links
import androidx.navigation.NavDeepLinkRequest
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
@Composable
fun DeepLinkExample() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home"
) {
composable(
"home",
deepLinks = listOf(
NavDeepLinkRequest.Builder
.fromUri("android-app://com.example.app/home")
.build()
)
) {
HomeScreen(navController)
}
}
}