<?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.
Related snips
package com.example.myapp
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
Dependency injection with Hilt
package com.example.myapp.ui
import androidx.lifecycle.*
import kotlinx.coroutines.launch
class UserViewModel(
LiveData transformations and mediators
package com.example.myapp.utils
import android.os.Build
import android.os.StrictMode
import android.os.Trace
import timber.log.Timber
Performance optimization and profiling
<nav class="site-nav" data-controller="preload">
<ul>
<li>
<%= link_to "Dashboard", dashboard_path,
class: "nav-link",
data: { turbo_preload: true } %>
Speed up perceived performance with Turbo preload links
package com.example.myapp.widget
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
App widgets for home screen
package com.example.myapp.data.repository
import com.example.myapp.data.local.PostDao
import com.example.myapp.data.local.PostEntity
import com.example.myapp.data.remote.ApiService
import com.example.myapp.models.Post
Unit testing with JUnit and MockK
Share this code
Here's the card — post it anywhere.