<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/nav_graph"
app:startDestination="@id/postsFragment">
<fragment
android:id="@+id/postsFragment"
android:name="com.example.myapp.ui.posts.PostsFragment"
android:label="@string/posts_title"
tools:layout="@layout/fragment_posts">
<action
android:id="@+id/action_posts_to_detail"
app:destination="@id/postDetailFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
<action
android:id="@+id/action_posts_to_create"
app:destination="@id/createPostFragment" />
</fragment>
<fragment
android:id="@+id/postDetailFragment"
android:name="com.example.myapp.ui.detail.PostDetailFragment"
android:label="@string/post_detail_title"
tools:layout="@layout/fragment_post_detail">
<argument
android:name="postId"
app:argType="integer" />
<argument
android:name="title"
app:argType="string"
app:nullable="true" />
<deepLink
app:uri="myapp://post/{postId}" />
</fragment>
<fragment
android:id="@+id/createPostFragment"
android:name="com.example.myapp.ui.create.CreatePostFragment"
android:label="@string/create_post_title"
tools:layout="@layout/fragment_create_post" />
<fragment
android:id="@+id/profileFragment"
android:name="com.example.myapp.ui.profile.ProfileFragment"
android:label="@string/profile_title"
tools:layout="@layout/fragment_profile" />
</navigation>
package com.example.myapp.ui.posts
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.myapp.R
import com.example.myapp.databinding.FragmentPostsBinding
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
@AndroidEntryPoint
class PostsFragment : Fragment(R.layout.fragment_posts) {
private var _binding: FragmentPostsBinding? = null
private val binding get() = _binding!!
private val viewModel: PostsViewModel by viewModels()
private val adapter = PostsAdapter(
onPostClick = { post -> navigateToDetail(post.id, post.title) }
)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentPostsBinding.bind(view)
setupRecyclerView()
setupObservers()
setupFab()
}
private fun setupRecyclerView() {
binding.recyclerView.apply {
layoutManager = LinearLayoutManager(context)
adapter = this@PostsFragment.adapter
}
}
private fun setupObservers() {
viewLifecycleOwner.lifecycleScope.launch {
viewModel.uiState.collect { state ->
adapter.submitList(state.posts)
binding.progressBar.visibility =
if (state.isLoading) View.VISIBLE else View.GONE
state.error?.let { error ->
// Show error message
}
}
}
}
private fun setupFab() {
binding.fab.setOnClickListener {
findNavController().navigate(
R.id.action_posts_to_create
)
}
}
private fun navigateToDetail(postId: Int, title: String) {
val action = PostsFragmentDirections
.actionPostsToDetail(postId, title)
findNavController().navigate(action)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
package com.example.myapp.ui
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import com.example.myapp.R
import com.example.myapp.databinding.ActivityMainBinding
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
// Setup bottom navigation
binding.bottomNav.setupWithNavController(navController)
// Setup toolbar
appBarConfiguration = AppBarConfiguration(
setOf(R.id.postsFragment, R.id.profileFragment)
)
setupActionBarWithNavController(navController, appBarConfiguration)
// Handle deep links
navController.handleDeepLink(intent)
}
override fun onSupportNavigateUp(): Boolean {
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
}
Navigation Component provides consistent navigation with type-safe argument passing. I define navigation graph in XML with destinations and actions. NavController handles navigation with navigate() and popBackStack(). Safe Args plugin generates type-safe classes for passing arguments between destinations. Deep links connect external URLs to destinations. Navigation UI integrates with Toolbar, BottomNavigationView, and DrawerLayout. Single Activity architecture simplifies lifecycle management. Nested graphs organize complex flows. Conditional navigation uses NavOptions for animations and pop behavior. The component handles Fragment transactions automatically, preventing common bugs. Compose Navigation brings these concepts to Jetpack Compose with composable destinations.