El component de navegació fa servir un graf de navegació per gestionar la navegació de la teva aplicació.

The Navigation component uses a navigation graph to manage your app's navigation. The navigation graph is a data structure that contains each destination within your app and the connections between them.

Note: The navigation graph is distinct from the back stack, which is a stack within the NavController that holds destinations the user has recently visited.

There are three general types of destinations: hosted, dialog, and activity.

Hosted destinations are the most common and fundamental destinations.

Hosted destination fills the entire navigation host. That is, the size of a hosted destination is the same as the size of the navigation host and previous destinations are not visible.

Add the Navigation dependency

Open the app's build file, found at app/build.gradle.

En la secció dependencies, afegeix la dependència amb navigation-compose:

dependencies {
  implementation("androidx.navigation:navigation-compose:2.8.4")
  // ...
}

You can find the latest version of navigation-compose here.

Screen

A continuació tens una aplicació amb dos "screens".

L'activitat principal comença amb la HomeScreen que demana el nom a l'usuari:

package dev.xtec.composenavigation

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import dev.xtec.composenavigation.ui.theme.ComposeNavigationTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            ComposeNavigationTheme {
                HomeScreen()
            }
        }
    }
}

@Composable
fun HomeScreen() {
    val text = remember { mutableStateOf("") }
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
        modifier = Modifier.fillMaxSize()
    ) {
        OutlinedTextField(value = text.value, onValueChange = {
            text.value = it
        })

        Button(
            onClick = {
            },
            modifier = Modifier.padding(vertical = 40.dp)
        ) {
            Text("Login")
        }
    }
}

@Composable
fun UserScreen(user: String) {
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
        modifier = Modifier.fillMaxSize()
    ) {
        Text("Hello $user")
    }
}

Al apretar el butó l'aplicació ha de presentar la UserScreen amb el nom de l'usuari.

Has de definir rutes mitjançant classes o objectes que han de ser serialitazbles.

  • Object: Utiliza un objecte per una ruta que no té paràmetres.

  • Class: Utilitza una classe per una ruta amb paràmetres

Afegeix les dependències de Serialització.

Afegeix aquestes rutes:

@Serializable
object Home

@Serializable
data class User(val name: String)

A continuació has de crear un graf de navegació amb la funció "composable" NavHost

// ...
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import kotlinx.serialization.Serializable

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            ComposeNavigationTheme {
                App()
            }
        }
    }
}

@Serializable
object Home

@Serializable
data class User(val name: String)

@Composable
fun App() {
    val controller = rememberNavController()
    NavHost(controller, startDestination = Home) {
        composable<Home> { HomeScreen(controller) }
        composable<User> { backStackEntry ->
            UserScreen(controller, backStackEntry.toRoute())
        }
    }
}

@Composable
fun HomeScreen(controller: NavController) {
    val text = remember { mutableStateOf("") }
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
        modifier = Modifier.fillMaxSize()
    ) {
        OutlinedTextField(value = text.value, onValueChange = {
            text.value = it
        })

        Button(
            onClick = {
            },
            modifier = Modifier.padding(vertical = 40.dp)
        ) {
            Text("Login")
        }
    }
}

@Composable
fun UserScreen(controller: NavController, user: User) {

    val user = "TODO"
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
        modifier = Modifier.fillMaxSize()
    ) {
        Text("Hello $user")
    }
}

Al cridar la funció "composable" NavHost passem com a paràmetres un NavController i una ruta pel destí incial (la ruta Home).

Amb la funció composable() defineixes quin "composable" es el destí d'una "ruta".

flowchart LR
    home([Home]) --> HomeScreen
    user(["User(name)"]) --> UserScreen

Quan defineixes una ruta mai has de dir de quin "screen" ha de venir, sinó quin "screen" a de gestionar la "ruta":

@Composable
fun App() {
    val controller = rememberNavController()
    NavHost(controller, startDestination = Home) {
        composable<Home> { HomeScreen(controller) }
        composable<User> { backStackEntry ->
            UserScreen(controller, backStackEntry.toRoute())
        }
    }
}

Si executes l'aplicació pots veure que el resultat és el mateix que abans.

La diferència és que ara HomeScreen te com a parèmetre un objecte de tipus NavController.

Modifica el codi perquè quan l'usuari faci click al butó l'aplicació navegui al "screen" que correspon a la ruta User:

Button(
    onClick = {
        controller.navigate(User(text.value))
    },
    modifier = Modifier.padding(vertical = 40.dp)
) {
    Text("Login")
}

Ara la UserScreen pot accedir a la ruta que s'ha utilitzat per activar-la, que és un objecte de tipus User.

@Composable
fun UserScreen(controller: NavController, user: User) {

    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
        modifier = Modifier.fillMaxSize()
    ) {
        Text("Hello ${user.name} !")
    }
}

TODO