Navigation

๐Ÿงช Create Navigation between composable screens

Compose multiplatform navigation library enable a navigation with navigation host

Add Navigation dependency to your project

gradle.build.kts (module : composeApp)
...
 commonMain.dependencies {
            ...
            implementation(libs.kotlin.navigation)
...

Create your navigation host

The navigation host is the configuration class that defines routes of your application.

Routes are path between all the composable screens that you will call later on your app.

routes overview

For this Hands-on Lab we need 3 routes for :

  • At startup to the WelcomeScreen
  • from Welcome screen to the QuizScreen
  • from the final question QuizScreento the ScoreScreen
App.kt (SourceSet: commonMain)

val questions = listOf(
    Question(
        1,
        "Android is a great platform ?",
        1,
        listOf(Answer(1, "YES"), Answer(2, "NO"))
    ),
    Question(
        1,
        "Android is a bad platform ?",
        2,
        listOf(Answer(1, "YES"), Answer(2, "NO"))
    )
)

@Serializable
object WelcomeRoute

@Serializable
object QuizRoute

@Serializable
data class ScoreRoute(val score: Int, val questionSize: Int)

@Composable
fun App(
    navController: NavHostController = rememberNavController()
) {

    MaterialTheme {
        NavHost(
            navController = navController,
            startDestination = WelcomeRoute,
        ) {
            composable<WelcomeRoute>() {
                welcomeScreen(
                    onStartButtonPushed = {
                        navController.navigate(route = QuizRoute)
                    }
                )
            }
             composable<QuizRoute>() {
                    questionScreen(
                        questions = questions,
                        /* FOR SPEAKER TALK DEMO ON WEB APP */
                        onFinishButtonPushed = {
                            score: Int, questionSize: Int -> navController.navigate(route = ScoreRoute(score, questionSize))
                        }
                    )
            }
            composable<ScoreRoute> { backStackEntry ->
                val scoreRoute: ScoreRoute = backStackEntry.toRoute<ScoreRoute>()
                scoreScreen(
                    score = scoreRoute.score,
                    total = scoreRoute.questionSize,
                    onResetButtonPushed = {
                        navController.navigate(route = QuizRoute)
                    }
                )
            }
        }
    }
}

WARNING

As you can see all composables now take as parameter a navigator. It will be needed to navigate with routes between screens.

for example, the WelcomeScreen composable is now declared as follows :

@Composable()
fun welcomeScreen(navigator: Navigator){
    ...

Use the navigation host

Use the callback

Use onStartButtonPushed declared on screen instantiation in the NavHost on welcome screen buttons click

WelcomeScreen.kt (SourceSet: commonMain)
fun welcomeScreen(onStartButtonPushed: () -> Unit) {
...

    Button(
        modifier = Modifier.padding(all = 10.dp),
        onClick = { onStartButtonPushed() }
    ) {
...

The same can be done for other screens

QuestionScreen.kt (commonMain)

fun questionScreen(questions: List<Question>, onFinishButtonPushed: (Int,Int) -> Unit) {
..
Button(
                modifier = Modifier.padding(bottom = 20.dp),
                onClick = {
                    /* FOR SPEAKER TALK DEMO ON WEB APP */
                    if (getPlatform().name == "WASM") {
                        onSaveStatQuestion(
                            questions[questionProgress].id,
                            questions[questionProgress].label,
                            selectedAnswer,
                            questions[questionProgress].correctAnswerId,
                            questions[questionProgress].answers[selectedAnswer.toInt() - 1].label
                        )
                    }

                    if (selectedAnswer == questions[questionProgress].correctAnswerId) {
                        score++
                    }
                    if (questionProgress < questions.size - 1) {
                        questionProgress++
                        selectedAnswer = 1
                    } else {
                        onFinishButtonPushed(score, questions.size)
                    }
                }
}
...
ScoreScreen.kt (SourceSet : commonMain)

fun scoreScreen(score: Int,total:Int,onResetButtonPushed: () -> Unit){
...
 Button(
     modifier = Modifier.padding(all = 20.dp),
    onClick = {
        onResetButtonPushed()
     }
 ) 
...

๐ŸŽฏ Solutions

Sources

The full solution for this section is availabe hereopen in new window

โœ… If everything is fine, congrats, you've just finish this codelab. You can now experiment your kotlin skills eveywhere !

๐Ÿ“– Further reading

Last Updated:
Contributors: Ibrahim Gharbi