Phase 3 · Navigation & ArchitectureModule 31~30 min read

Navigation in Compose

Move between screens with Navigation Compose, routes, arguments, and the back stack.

Goal and setup

Open a detail screen from a list and return with Back. You will pass a small identifier between two destinations using typed Navigation Compose routes.

Start with your working Compose project. Add Navigation Compose and Kotlin serialization JSON dependencies, then apply the Kotlin serialization compiler plugin using the same version as your Kotlin plugin. The phase 3 project includes the exact build configuration. Keep that compatible set together.

What this example includes

Place the following declarations in one Kotlin file. Call LessonNavigation inside your existing theme and Scaffold content, with Scaffold padding applied by the parent. No ViewModel, database, or internet permission is needed for this first navigation example.

Controller, host, and routes

  • NavController owns navigation history. Use rememberNavController to retain the controller across recompositions.
  • NavHost connects route types to the composables that display their content.
  • A route identifies a destination. LessonList needs no arguments; LessonDetails carries a lessonId.
  • The back stack records the destination history. Popping the current entry returns to the previous destination.

A two-screen example

LessonNavigation.kt
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.toRoute
import kotlinx.serialization.Serializable

@Serializable object LessonList
@Serializable data class LessonDetails(val lessonId: Int)

data class LessonSummary(val id: Int, val title: String)
private val lessons = listOf(
    LessonSummary(1, "Compose Text"),
    LessonSummary(2, "Rows and Columns"),
)

@Composable
fun LessonNavigation() {
    val controller = rememberNavController()
    NavHost(controller, startDestination = LessonList) {
        composable<LessonList> {
            Column {
                lessons.forEach { lesson ->
                    Button(onClick = {
                        controller.navigate(LessonDetails(lesson.id))
                    }) { Text(lesson.title) }
                }
            }
        }
        composable<LessonDetails> { entry ->
            val route = entry.toRoute<LessonDetails>()
            val lesson = lessons.find { it.id == route.lessonId }
            Column {
                Text(lesson?.title ?: "Lesson not found")
                TextButton(onClick = { controller.popBackStack() }) {
                    Text("Back to lessons")
                }
            }
        }
    }
}
Complete navigation component with imports. Call it from the content area of your existing Activity.

Follow one selection

  1. The host begins at LessonList and displays the two bundled titles.
  2. Selecting Rows and Columns calls navigate with LessonDetails(2).
  3. The detail destination reads the argument using toRoute and looks up ID 2.
  4. The screen displays the current matching title. A missing ID produces a readable message.
  5. The visible Back button calls popBackStack. Android system Back also returns through the navigation history.

The Serializable annotation lets Navigation encode the route arguments. It does not save your application records. Pass the ID and look up the current record rather than carrying a second mutable copy through navigation.

Keep callbacks at the screen boundary

For a reusable screen, accept an onOpenLesson callback instead of passing the controller into every row. The phase project uses this separation so that UI components can be previewed independently.

Practice and checks

Add a third lesson with ID 3 and open it from the list. Keep the route unchanged.

  • Select each title: its matching title appears on detail.
  • Use the visible Back control and system Back separately: each returns to the list.
  • Temporarily navigate to ID 999: Lesson not found appears and Back remains available. Restore the normal click handler afterward.
  • Rotate while on detail: the controller can restore its destination and arguments. This does not create durable lesson data.

If a route cannot be serialized, check the annotation and serialization plugin. If a title is incorrect, inspect the ID passed by the callback and the lookup before changing the navigation graph.

Recap and quick check

Key takeaways

  • A controller owns navigation history; a host displays destinations.
  • Use small route arguments such as an ID.
  • Lookups can fail: show useful fallback content and a Back action.
  • Route restoration and permanent data storage solve different problems.

Quick check

1. What should a lesson detail route carry?

2. What does popBackStack do here?

Reference: type-safe Navigation Compose routes.