Jetpack Compose: Navigate with argument

Introduction

In modern Android app development, Jetpack Compose has emerged as a powerful and declarative UI framework, offering a fresh approach to building Android user interfaces. One of its most exciting features is seamless navigation between different screens within an app, including the ability to pass data, or arguments, between these screens. This capability is particularly useful for creating dynamic and interactive applications. In this article, we will explore a practical example of using Jetpack Compose to navigate between screens with arguments, written in Kotlin. The code demonstrates how to manage navigation from a list of color names to a detailed view of each color, highlighting the simplicity and flexibility of Jetpack Compose's navigation system.

This tutorial will break down the Kotlin code example step by step, explaining how navigation works in a Compose app, how to pass arguments between composables, and how to structure your app’s UI efficiently using Jetpack Compose’s NavHost, composable, and Scaffold components. By the end, you will have a good understanding of how to implement similar navigation patterns in your own apps.

MainActivity: Entry Point of the App

The code begins in the MainActivity class, which is the entry point of the application. Here, the onCreate method calls setContent, which is a Composable function that sets up the UI of the app using Jetpack Compose. This method replaces the traditional setContentView used in XML-based layouts. The core UI is managed by the GetScaffold composable, which defines the app's overall structure using the Scaffold component.

GetScaffold: Structuring the UI

The GetScaffold composable provides the layout structure for the app. It uses the Scaffold component, a common layout pattern in Jetpack Compose that allows you to define key UI elements such as the TopAppBar, content, and background color. In this case, the top bar displays a title ("Compose - Navigation Argument"), and the main content of the screen is determined by the MainContent composable. Additionally, a navigation controller (NavHostController) is created using rememberNavController to manage the navigation between different screens.

MainNavigation: Setting Up Navigation Routes

The heart of the navigation logic is in the MainNavigation composable. This function uses the NavHost component to define the navigation graph, which consists of different routes or destinations. The startDestination is set to "listScreen", indicating that the app should display the list of colors first. Two routes are defined in the NavHost:

  1. ListScreen: Displays a list of color names.
  2. DetailsScreen: Displays the details of a selected color, accepting an argument (colorName) passed via the route.

ListScreen: Displaying Color Options

The ListScreen composable displays a list of color names, which are defined in a simple list (list). It uses a Column layout to arrange the color options vertically. Each color name is wrapped in a Card component to make it clickable, allowing users to select a color. When a user clicks on a color, the app navigates to the detailsScreen route using the navController.navigate method, passing the selected color as an argument (detailsScreen/$it). This demonstrates how easy it is to define dynamic navigation routes in Jetpack Compose.

DetailsScreen: Showing Color Details

The DetailsScreen composable is responsible for displaying the details of the selected color. It retrieves the colorName argument from the navigation back stack using backStackEntry.arguments?.getString("colorName"). The colorName is then displayed in the center of the screen using a Text composable, making this screen dynamic based on the color selected by the user. This composable is a great example of how to receive and display arguments passed through navigation.

Summary

This Kotlin example demonstrates how to use Jetpack Compose’s powerful navigation features to navigate between screens while passing arguments. By leveraging the NavHost, composable, and NavController, developers can build highly interactive and dynamic Android apps with minimal boilerplate. The use of Jetpack Compose allows for a more intuitive and declarative way to manage UI elements, and passing arguments through navigation simplifies the process of creating dynamic content.

Jetpack Compose is changing the way Android apps are built, and this example illustrates just one of the many ways it can streamline app development. Whether you're building a small app with a few screens or a large, complex project, the flexibility and simplicity of Compose’s navigation system make it an excellent choice for modern Android development.


MainActivity.kt

package com.cfsuman.jetpackcompose

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController


class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            GetScaffold()
        }
    }


    @Composable
    fun GetScaffold(){
        val navController:NavHostController = rememberNavController()

        Scaffold(
            topBar = {
                TopAppBar(
                    title = { Text(
                        text = "Compose - Navigation Argument"
                    )},
                    backgroundColor = Color(0xFFC0E8D5),
                )
            },
            content = {MainContent(navController)},
            backgroundColor = Color(0xFFEDEAE0),
        )
    }


    @Composable
    fun MainContent(navController: NavHostController){
        Box(
            modifier = Modifier.fillMaxSize(),
        ){
            MainNavigation(navController)
        }
    }


    @Composable
    fun MainNavigation(navController: NavHostController){
        NavHost(
            navController = navController,
            startDestination = "listScreen"
        ){
            composable("listScreen"){ListScreen(navController)}

            composable("detailsScreen/{colorName}"
            ){ backStackEntry ->
                DetailsScreen(
                    backStackEntry.arguments?.getString("colorName")
                )
            }
        }
    }


    @Composable
    fun ListScreen(navController: NavController){
        val list = listOf("Red","Green","Blue","Yellow","Black")
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(4.dp),
            verticalArrangement = Arrangement.spacedBy(4.dp),
            horizontalAlignment = Alignment.Start
        ) {
            Text(
                text = "List Screen",
                style = MaterialTheme.typography.h4,
                modifier = Modifier.padding(bottom = 24.dp)
            )

            list.forEach {
                Card(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(1.dp)
                        .height(50.dp)
                        .clickable {
                            navController.navigate("detailsScreen/$it")
                        }
                ) {
                    Box(modifier = Modifier
                        .fillMaxSize()
                        .wrapContentSize(Alignment.Center)) {
                        Text(text = it)
                    }
                }
            }
        }
    }


    @Composable
    fun DetailsScreen(colorName:String?){
        Box(
            modifier = Modifier.fillMaxSize().padding(4.dp),
            contentAlignment = Alignment.Center
        ) {
            Text(
                text = "Details Screen\n${colorName?.apply{}}",
                style = MaterialTheme.typography.h4,
                modifier = Modifier.padding(bottom = 24.dp),
                textAlign = TextAlign.Center
            )
        }
    }
}
More android jetpack compose tutorials