Jetpack Compose: Navigation with Object Arguments
Jetpack Compose is a powerful toolkit for building native Android UIs with a declarative approach. It simplifies UI creation by leveraging Kotlin code instead of XML layouts. In this article, we will explore an interesting feature of Jetpack Compose: passing complex objects through navigation using Gson, focusing on how to pass an object between screens in a Compose-based application.
Navigation in Jetpack Compose provides a simple way to handle navigation between different composables. However, unlike primitive types like strings or integers, passing more complex data such as objects requires a bit more setup. In this example, we'll see how to pass an object by serializing it into a JSON string and restoring it on the target screen using Gson, a popular JSON library in Android.
MainActivity: Entry Point and Scaffold Setup
The main entry point for the application is the MainActivity
, which extends AppCompatActivity
. Inside the onCreate
method, we use setContent
to define the UI using Composable functions. The root composable, GetScaffold
, contains a Scaffold
, which is a common layout structure in Jetpack Compose that allows setting up material components such as a TopAppBar
and content.
In GetScaffold
, the TopAppBar
displays a title, and the main content is controlled by MainContent
, which accepts a NavHostController
to manage navigation between different screens. The background colors of the Scaffold and the TopAppBar
are set using the Color
class, adding visual consistency to the UI.
Navigation Setup
The navigation logic is handled within the MainNavigation
composable. This composable defines a NavHost
that manages the navigation structure, with listScreen
being the start destination. The composable
function is used to define individual screens, and in this case, we have two screens: ListScreen
and DetailsScreen
.
To pass an object to the DetailsScreen
, we define a route that includes a placeholder for a string representation of the object (colorObject
). When navigating to the details screen, this string is retrieved from the back stack entry arguments. The string is then deserialized back into a ColorObject
using the Gson library.
ListScreen: Displaying and Navigating with Objects
ListScreen
is where a list of color objects is displayed to the user. These objects are defined using the ColorObject
data class, which contains a color name and a hex color value. A Column
composable arranges the color cards vertically, with spacing and padding applied for better readability.
Each card in the list is clickable, and when clicked, it triggers the navigation to DetailsScreen
. Before navigating, the selected ColorObject
is serialized into a JSON string using Gson. This string is then passed through the navigation system as a route argument.
DetailsScreen: Displaying the Selected Object
The DetailsScreen
composable is responsible for displaying the color object passed from the ListScreen
. It accepts a ColorObject?
as an argument and displays the color's name and background. The background color of the screen is dynamically set using the hex value from the ColorObject
. If no object is passed, the screen defaults to a white background.
The text inside the screen is centered using a Box
composable with the fillMaxSize
modifier. The Text
composable shows the color name in a larger font and is aligned to the center of the screen for simplicity.
Summary
In this example, we've demonstrated how to pass objects between screens using Jetpack Compose's navigation component and the Gson library. The key takeaway is that objects need to be serialized into a string format (JSON) before passing them as arguments in navigation, as Jetpack Compose's navArgument
only supports primitive types. On the receiving side, we can deserialize the JSON string back into an object.
Jetpack Compose's declarative approach makes UI development more intuitive, and combining it with the power of Kotlin and libraries like Gson opens up possibilities for building dynamic and responsive applications. This example showcases how easy it can be to integrate navigation with complex data passing in a Compose-based Android app.
package com.cfsuman.jetpackcompose
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
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.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.google.gson.Gson
class MainActivity : AppCompatActivity() {
override fun onCreate(savedObjectState: Bundle?) {
super.onCreate(savedObjectState)
setContent {
GetScaffold()
}
}
@Composable
fun GetScaffold(){
val navController:NavHostController = rememberNavController()
Scaffold(
topBar = {
TopAppBar(
title = { Text(
text = "Compose - Nav Object 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(
route = "detailsScreen/{colorObject}",
arguments = listOf(
navArgument("colorObject")
{ type = NavType.StringType },
)
){ backStackEntry ->
backStackEntry.arguments
?.getString("colorObject").let { json->
val colorObject = Gson().fromJson(
json,ColorObject::class.java
)
DetailsScreen(colorObject = colorObject)
}
}
}
}
@Composable
fun ListScreen(navController: NavController){
fun navigateToColorObject(colorObject: ColorObject){
val colorJson = Gson().toJson(colorObject)
navController.navigate("detailsScreen/$colorJson")
}
val colors = listOf<ColorObject>(
ColorObject("Brick red",0xFFCB4154),
ColorObject("Baby blue",0xFF89CFF0),
ColorObject("Brilliant rose",0xFFFF55A3),
ColorObject("Cadmium orange",0xFFED872D),
ColorObject("Cameo pink",0xFFEFBBCC)
)
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)
)
colors.forEach {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(1.dp)
.height(75.dp)
.clickable {
navigateToColorObject(it)
}
) {
Box(modifier = Modifier
.fillMaxSize()
.wrapContentSize(Alignment.Center)) {
Text(
text = it.name,
fontWeight = FontWeight.Bold
)
}
}
}
}
}
@Composable
fun DetailsScreen(colorObject:ColorObject?){
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(colorObject?.value ?: 0xFFFFFF)),
contentAlignment = Alignment.Center
) {
Text(
text = colorObject?.name?:"",
style = MaterialTheme.typography.h4,
modifier = Modifier.padding(bottom = 24.dp),
textAlign = TextAlign.Center
)
}
}
}
data class ColorObject(
val name:String,
val value:Long
)
implementation 'androidx.navigation:navigation-compose:2.4.0-alpha10'
implementation 'com.google.code.gson:gson:2.8.6'
- jetpack compose - How to use navigation controller
- jetpack compose - Navigate with argument
- jetpack compose - Navigation multiple arguments
- jetpack compose - Navigation arguments data type
- jetpack compose - How to change StatusBar color
- jetpack compose - How to change NavigationBar color
- jetpack compose - How to change SystemBars color
- jetpack compose - Kotlinx serialization handle null values
- jetpack compose - Kotlinx serialization not encode null values
- jetpack compose - Kotlinx serialization encode to string
- jetpack compose - Kotlinx serialization decode josn element
- jetpack compose - How to use kotlin flow
- jetpack compose - Random number flow
- jetpack compose - Icon from vector resource
- jetpack compose - IconButton from vector resource