Jetpack Compose Navigation: Your Guide to Seamless Screen Hopping!

Novjean Kannathara
3 min readJun 30, 2024

Navigating through mobile apps can be tricky, but Jetpack Compose makes it a breeze! Whether you’re new to Android development or just Jetpack Compose, this guide will help you master navigation in no time.

Getting Started with Navigation in Jetpack Compose

When it comes to navigation in Jetpack Compose, the core components you need to know about are NavHost, NavController, and startDestination. Let’s break these down.

  1. NavHost: Think of this as the central hub of your app’s navigation. It hosts all your composable screens and manages the transitions between them.
  2. NavController: This is the traffic cop of your app, controlling the flow of navigation between different composables.
  3. startDestination: The first screen that appears when your app launches.

To manage your routes efficiently, we’ll define a sealed class called Screen and have the routes specified within it.

Setting Up Navigation

Here’s how to get your navigation set up:

  • Define Your Screens: Each screen in your app will be a composable. You’ll define a sealed class to handle these routes.
// Screen.kt
sealed class Screen(val route: String) {
object Home : Screen("home")
object Details : Screen("details")
}
  • Create Your NavHost: This will host your navigation graph.
// MainActivity.kt
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.*
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Screen.Home.route) {
composable(Screen.Home.route) { HomeScreen(navController) }
composable(Screen.Details.route) { DetailsScreen(navController) }
}
}
}
}
  • Control Navigation: Use NavController to navigate between screens.
// HomeScreen.kt
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.navigation.NavController

@Composable
fun HomeScreen(navController: NavController) {
Button(onClick = { navController.navigate(Screen.Details.route) }) {
Text("Go to Details")
}
}
// DetailsScreen.kt
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.navigation.NavController
@Composable
fun DetailsScreen(navController: NavController) {
Button(onClick = { navController.popBackStack() }) {
Text("Back to Home")
}

Passing Arguments

Sometimes, you’ll need to pass data between screens. Jetpack Compose makes this easy too.

  • Define Routes with Arguments: You can specify arguments in your routes.
// Screen.kt
sealed class Screen(val route: String) {
object Home : Screen("home")
object Details : Screen("details/{name}")
}
  • Pass Arguments During Navigation: Use a helper function to format the route with arguments.
// HomeScreen.kt
@Composable
fun HomeScreen(navController: NavController) {
val name = "Jetpack"
Button(onClick = { navController.navigate(Screen.Details.withArgs(name)) }) {
Text("Go to Details")
}
}
  • Retrieve Arguments in the Composable: Use the NavBackStackEntry to access arguments.
// DetailsScreen.kt
import androidx.compose.runtime.Composable
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import androidx.navigation.NavType
@Composable
fun DetailsScreen(navController: NavController, name: String?) {
Text(text = "Hello, $name!")
}
// Updated MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Screen.Home.route) {
composable(Screen.Home.route) { HomeScreen(navController) }
composable(
route = Screen.Details.route,
arguments = listOf(navArgument("name") { type = NavType.StringType })
) { backStackEntry ->
DetailsScreen(navController, backStackEntry.arguments?.getString("name"))
}
}
}
}
}
// Helper function in Screen.kt
fun Screen.withArgs(vararg args: String): String {
return buildString {
append(route)
args.forEach { arg ->
append("/$arg")
}
}
}

Putting It All Together

In the MainActivity, we set up the NavHost and define the composable destinations using the NavController. The HomeScreen and DetailsScreen composables contain buttons that navigate to different screens using NavController. Additionally, we pass and retrieve arguments seamlessly between screens.

Tips and Tricks

  • Sealed Classes for Routes: Using a sealed class for your routes ensures type safety and easier navigation management.
  • Composable Functions: Keep your composables clean and separated. Each screen should have its own function.
  • Navigation Transitions: Customize transitions between screens for a smoother user experience.
  • Argument Passing: Use helper functions to manage and format routes with arguments efficiently.

Wrapping Up

Navigating in Jetpack Compose might seem daunting at first, but once you get the hang of it, you’ll be zipping through your app like a pro. Stay tuned for more advanced features in Jetpack Compose in our next article!

Happy coding! 🚀

About the Author

Novjean Kannathara is a passionate Mobile App developer with a knack for teaching and a love for clean code. Follow him on Twitter and connect on LinkedIn.

--

--