Implementing Clickable Items in LazyRow with Jetpack Compose

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, a println) 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:

  1. 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)
        }
    }
  2. 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")
                        }
                )
            }
        }
    }
  3. 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!