Jetpack Compose has revolutionized Android development with its declarative approach, allowing developers to create rich and complex UIs with less code. One of the most powerful features in Jetpack Compose is the LazyRow component, which efficiently displays horizontally scrollable lists. A common requirement when working with LazyRow is implementing clickable items that trigger actions or navigate to different parts of your app. In this post, we’ll explore how to achieve this effectively, with a focus on best practices and advanced use cases.
What is LazyRow?
LazyRow is a composable function in Jetpack Compose that creates a horizontally scrollable list. It’s highly efficient because it only composes and lays out the visible items, making it ideal for lists with a large number of elements. LazyRow is often used for creating carousels, horizontal grids, or item pickers.
Basic Structure of LazyRow
Here’s a simple example of a LazyRow:
LazyRow {
items(listOf("Item 1", "Item 2", "Item 3")) { item ->
Text(text = item, modifier = Modifier.padding(8.dp))
}
}
This code displays a horizontal list of text items. While straightforward, it’s not interactive yet. Let’s dive into making these items clickable.
Making LazyRow Items Clickable
To make items in a LazyRow clickable, we use the Modifier.clickable
modifier. This modifier allows you to define actions that should be triggered when an item is tapped.
Adding Clickable Behavior
Below is an example of implementing clickable items:
val items = listOf("Item 1", "Item 2", "Item 3")
LazyRow {
items(items) { item ->
Text(
text = item,
modifier = Modifier
.padding(8.dp)
.clickable {
println("Clicked on $item")
}
)
}
}
In this example:
Modifier.clickable
listens for click events.The action inside
clickable
(here, aprintln
) gets executed when an item is tapped.
Navigating with Clickable Items
In most real-world applications, clicking an item in a LazyRow often leads to navigation or triggers a complex action. Let’s see how to integrate Jetpack Navigation with clickable items.
Example: Navigating to a Details Screen
Assume we have a list of products, and clicking on a product navigates to a details screen. Here’s how you can set this up:
Set Up Navigation: Define a NavHost with a composable for the details screen:
NavHost(navController = navController, startDestination = "home") { composable("home") { HomeScreen(navController) } composable("details/{item}") { backStackEntry -> val item = backStackEntry.arguments?.getString("item") DetailsScreen(item) } }
Create the LazyRow with Navigation: Pass the
NavController
to the LazyRow and navigate on click:@Composable fun HomeScreen(navController: NavController) { val items = listOf("Item 1", "Item 2", "Item 3") LazyRow { items(items) { item -> Text( text = item, modifier = Modifier .padding(8.dp) .clickable { navController.navigate("details/$item") } ) } } }
Display the Details Screen:
@Composable fun DetailsScreen(item: String?) { Text(text = "Details for $item", modifier = Modifier.padding(16.dp)) }
This approach demonstrates how to pass arguments to another composable and dynamically display details based on the selected item.
Enhancing the User Experience
To create a polished experience, consider the following enhancements:
Adding Visual Feedback
Clickable items should provide feedback to users. Use indication
and interactionSource
to customize the feedback:
val interactionSource = remember { MutableInteractionSource() }
val isPressed = interactionSource.collectIsPressedAsState().value
Text(
text = item,
modifier = Modifier
.padding(8.dp)
.clickable(interactionSource = interactionSource, indication = null) {
println("Clicked on $item")
}
.background(if (isPressed) Color.Gray else Color.Transparent)
)
This example changes the background color of the item when pressed, providing a tactile feel.
Supporting Accessibility
Always include content descriptions for clickable items to make your app accessible:
Text(
text = item,
modifier = Modifier
.padding(8.dp)
.clickable(onClickLabel = "Click $item") {
println("Clicked on $item")
},
contentDescription = "Clickable item: $item"
)
Optimizing for Performance
For large data sets, consider:
Using a
remember
block to cache data and avoid recomposing the list unnecessarily.Avoiding heavy computations inside
LazyRow
lambdas.
Advanced Use Cases
Handling Long Press Actions
In addition to single taps, you can handle long presses using Modifier.combinedClickable
:
LazyRow {
items(items) { item ->
Text(
text = item,
modifier = Modifier
.padding(8.dp)
.combinedClickable(
onClick = { println("Clicked on $item") },
onLongClick = { println("Long pressed $item") }
)
)
}
}
Creating Custom Item Layouts
You can design complex layouts for each item in the LazyRow. For instance, a card with an image, title, and description:
LazyRow {
items(items) { item ->
Card(
modifier = Modifier
.padding(8.dp)
.clickable { println("Clicked on $item") },
elevation = 4.dp
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = item, style = MaterialTheme.typography.h6)
Text(text = "Description of $item", style = MaterialTheme.typography.body2)
}
}
}
}
Conclusion
LazyRow is a versatile and efficient tool for creating horizontally scrollable lists in Jetpack Compose. By combining it with clickable modifiers, navigation components, and advanced techniques like visual feedback and accessibility support, you can build highly interactive and polished user interfaces. Experiment with these techniques in your next project to create stunning, responsive designs.
Happy coding!