Skip to content

Navigate to detail

Hub › Android › Intermediate › Navigate to detail

Goal

Wire the list and detail screens together with Navigation Compose. After this page you have a two-destination app: the list screen navigates to a detail screen when a row is tapped, and the system back button returns to the list.

Prerequisites

AppNav — the navigation host

kotlin
import androidx.compose.runtime.Composable
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

@Composable
fun AppNav() {
    val nav = rememberNavController()
    NavHost(nav, startDestination = "list") {
        composable("list") {
            BookListScreen(onBookClick = { id -> nav.navigate("detail/$id") })
        }
        composable("detail/{id}") { entry ->
            val id = entry.arguments?.getString("id")?.toIntOrNull() ?: 0
            BookDetailScreen(id)
        }
    }
}

NavHost is the container that swaps composables based on the current back-stack destination. rememberNavController() creates the controller and survives recompositions. startDestination = "list" is the first screen shown.

Route strings use the "detail/{id}" template. Navigation Compose extracts the {id} segment from the URL and makes it available via entry.arguments. The ?: 0 default handles a malformed route gracefully — the detail screen will show "Loading…" if no book matches id 0.

BookDetailScreen

kotlin
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun BookDetailScreen(id: Int) {
    var book by remember { mutableStateOf<Book?>(null) }
    LaunchedEffect(id) { book = BookRepository().getBook(id) }
    Column(Modifier.padding(16.dp)) {
        Text(
            book?.title ?: "Loading…",
            style = MaterialTheme.typography.headlineSmall,
        )
        book?.let { Text(it.author) }
    }
}

LaunchedEffect(id) runs the suspend block when the composable first enters composition, and re-runs it if id changes. This is the correct way to trigger a side-effect (a suspend function call) from a composable. The book arrives after the repository's simulated delay and the composable redraws — the "Loading…" placeholder flashes briefly, then the title and author appear.

How the back stack works

Pressing the system Back button pops "detail/{id}" off the back stack, which brings the "list" destination back into view. You do not need to write any back-navigation code — NavHost handles it.

Wiring AppNav into MainActivity

In MainActivity.kt, replace the setContent body:

kotlin
setContent {
    MaterialTheme {
        AppNav()
    }
}

AppNav owns the NavHost, so every navigation decision flows through it.

Next07 Run and preview