Skip to content

DataCode-Academy/android-kotlin-lesson4

Repository files navigation

Lesson 4 — Navigation (navigation-compose)

Overview

This lesson demonstrates Jetpack Compose Navigation with practical examples of:

  • Setting up NavHost
  • Defining composable routes
  • Passing arguments between screens
  • Back navigation

Screenshots

Here's what you'll build in this lesson:

Home Screen Profile Screen Details Screen Details Example

Screens from left to right:

  1. Home Screen - Starting point with navigation options
  2. Profile Screen - Simple navigation without arguments
  3. Details Screen - Navigation with arguments (Item ID: 1, Name: Kotlin)
  4. Details Screen - Another example (Item ID: 3, Name: Navigation)

Project Structure

app/src/main/java/com/example/androidkotlinlesson4/
├── MainActivity.kt                 # Entry point with NavController setup
├── navigation/
│   ├── Screen.kt                   # Sealed class defining all routes
│   └── NavigationGraph.kt          # NavHost configuration
└── screens/
    ├── HomeScreen.kt               # Start destination with navigation options
    ├── ProfileScreen.kt            # Simple screen without arguments
    └── DetailsScreen.kt            # Screen receiving navigation arguments

Key Concepts

1. Navigation Dependencies

Added to gradle/libs.versions.toml:

navigationCompose = "2.8.0"
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }

2. Screen Routes (Screen.kt)

A sealed class defines all navigation destinations:

sealed class Screen(val route: String) {
    object Home : Screen("home")
    object Profile : Screen("profile")
    object Details : Screen("details/{itemId}/{itemName}") {
        fun createRoute(itemId: Int, itemName: String) = "details/$itemId/$itemName"
    }
}

Why sealed class?

  • Type-safe route definitions
  • Compile-time route checking
  • Easy to maintain and refactor

3. Navigation Graph (NavigationGraph.kt)

The NavHost defines all routes and their composables:

@Composable
fun NavigationGraph(navController: NavHostController) {
    NavHost(
        navController = navController,
        startDestination = Screen.Home.route
    ) {
        // Define all composable destinations here
    }
}

Key components:

  • NavHost: Container for navigation destinations
  • startDestination: Initial screen to display
  • composable(): Defines each navigable screen

4. Simple Navigation (No Arguments)

Navigate to ProfileScreen without passing data:

// In NavigationGraph
composable(route = Screen.Profile.route) {
    ProfileScreen(
        onNavigateBack = {
            navController.popBackStack()
        }
    )
}

// In HomeScreen - trigger navigation
Button(onClick = { navController.navigate(Screen.Profile.route) }) {
    Text("Go to Profile")
}

5. Navigation with Arguments

Pass multiple arguments to DetailsScreen:

Define route with parameters:

object Details : Screen("details/{itemId}/{itemName}") {
    fun createRoute(itemId: Int, itemName: String) = "details/$itemId/$itemName"
}

Configure composable with arguments:

composable(
    route = Screen.Details.route,
    arguments = listOf(
        navArgument("itemId") { type = NavType.IntType },
        navArgument("itemName") { type = NavType.StringType }
    )
) { backStackEntry ->
    val itemId = backStackEntry.arguments?.getInt("itemId") ?: 0
    val itemName = backStackEntry.arguments?.getString("itemName") ?: ""
    
    DetailsScreen(
        itemId = itemId,
        itemName = itemName,
        onNavigateBack = { navController.popBackStack() }
    )
}

Navigate with arguments:

navController.navigate(Screen.Details.createRoute(id, name))

Screens Breakdown

HomeScreen

  • Purpose: Starting destination with navigation examples
  • Features:
    • Button to navigate to Profile (no args)
    • List of items that navigate to Details (with args)
    • Demonstrates both navigation patterns

ProfileScreen

  • Purpose: Simple destination demonstrating back navigation
  • Features:
    • Top app bar with back button
    • User profile information display
    • Back button in content area

DetailsScreen

  • Purpose: Demonstrates receiving and using navigation arguments
  • Features:
    • Displays received itemId and itemName
    • Shows how arguments are passed through routes
    • Educational content about argument passing

Navigation Methods

1. Navigate Forward

navController.navigate(route)

2. Navigate Back

navController.popBackStack()

3. Navigate with Arguments

navController.navigate("details/$itemId/$itemName")
// OR using helper function
navController.navigate(Screen.Details.createRoute(itemId, itemName))

Argument Types Supported

  • IntType: Integer values
  • StringType: String values
  • BoolType: Boolean values
  • FloatType: Float values
  • LongType: Long values

Best Practices

  1. Use Sealed Classes for Routes: Type-safe and maintainable
  2. Create Helper Functions: For routes with arguments
  3. Define Arguments Explicitly: Specify type for each argument
  4. Handle Null Cases: Use default values when extracting arguments
  5. Separate Navigation Logic: Keep NavHost in separate file
  6. Pass Callbacks: Use lambda parameters for navigation actions

How to Run

  1. Sync Gradle: Ensure navigation-compose dependency is downloaded

    • File → Sync Project with Gradle Files
  2. Build and Run: Deploy to emulator or device

    • Run → Run 'app'
  3. Test Navigation:

    • Click "Go to Profile" for simple navigation
    • Click any item in the list to navigate with arguments
    • Use back button or "Go Back" to return

Common Navigation Patterns

Pattern 1: Single Screen Navigation

Button(onClick = { navController.navigate(Screen.Profile.route) })

Pattern 2: Navigation with Arguments

Card(onClick = { 
    navController.navigate(Screen.Details.createRoute(id, name)) 
})

Pattern 3: Pop Back Stack

IconButton(onClick = { navController.popBackStack() })

Pattern 4: Pop to Specific Route

navController.popBackStack(Screen.Home.route, inclusive = false)

Learning Outcomes

After completing this lesson, you will understand:

NavHost and NavController Setup

  • How to create and configure NavController using rememberNavController()
  • How to set up NavHost with a start destination
  • Understanding the navigation lifecycle

Navigation Routes

  • Defining routes using sealed classes for type safety
  • Creating simple routes without parameters
  • Creating routes with multiple parameters
  • Using helper functions to build parameterized routes

Navigate Between Screens

  • Using navController.navigate() to move forward
  • Passing data through navigation arguments
  • Understanding the navigation back stack

Argument Passing

  • Defining argument types (Int, String, Boolean, etc.)
  • Extracting arguments from BackStackEntry
  • Handling optional arguments with default values
  • Best practices for type-safe argument passing

Back Navigation

  • Using navController.popBackStack() to go back
  • Implementing back buttons in TopAppBar
  • Understanding when to use popBackStack() vs navigateUp()

Code Organization

  • Separating navigation logic from UI components
  • Using callbacks for navigation actions
  • Keeping screens independent and reusable
  • Structuring a scalable navigation architecture

Key Takeaways

🎯 Navigation is State Management

  • NavController maintains the navigation state
  • Each screen is a destination in the back stack
  • Arguments are part of the saved state

🎯 Type Safety Matters

  • Use sealed classes for routes to prevent typos
  • Define argument types explicitly
  • Leverage Kotlin's type system for safer navigation

🎯 Separation of Concerns

  • Screens should not know about NavController directly
  • Pass navigation callbacks as lambda parameters
  • Keep navigation logic in the NavigationGraph

🎯 User Experience

  • Always provide a way to go back
  • Use consistent navigation patterns
  • Handle the system back button automatically

Practice Exercises

Try these exercises to reinforce your learning:

  1. Add a Settings Screen

    • Create a new route in Screen.kt
    • Add a composable destination in NavigationGraph.kt
    • Add a button from HomeScreen to navigate to Settings
  2. Pass More Arguments

    • Modify DetailsScreen to accept a description parameter
    • Update the route definition and argument parsing
    • Pass the description from HomeScreen
  3. Implement Navigate Up

    • Try using navController.navigateUp() instead of popBackStack()
    • Understand the difference between the two methods
  4. Add Validation

    • Add validation for arguments (e.g., itemId must be positive)
    • Handle invalid arguments gracefully
    • Show error messages when needed
  5. Experiment with Back Stack

    • Use popBackStack(route, inclusive) to skip screens
    • Try navigating with launchSingleTop = true
    • Understand how to clear the back stack

Common Issues and Solutions

Problem: Unresolved reference errors for navigation imports ✅ Solution: Sync Gradle files after adding dependencies

Problem: Arguments are null in destination screen ✅ Solution: Check argument names match exactly in route definition and extraction

Problem: App crashes when navigating with arguments ✅ Solution: Ensure argument types are defined correctly in the composable

Problem: Back button doesn't work as expected ✅ Solution: Use navController.popBackStack() or let the system handle it automatically

Resources

Summary

In this lesson, you've learned how to implement navigation in Jetpack Compose using the Navigation Component. You can now:

  • Set up a navigation graph with multiple destinations
  • Navigate between screens with and without arguments
  • Extract and use navigation arguments
  • Implement proper back navigation
  • Organize your navigation code following best practices

Navigation is a fundamental part of Android app development, and mastering these concepts will help you build more complex, multi-screen applications with confidence!

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages