I'm working on a Jetpack Compose app where I have a LazyColumn displaying a list of items. Each item is wrapped in a Surface with a clickable modifier, but clicking the items doesn't seem to work. I've added debugging logs, and it appears the clickable function is never entered—nothing logs when I tap the items.
Here's my code:
import android.util.Log
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.example.namecheap_training.model.Product
@Composable
fun ProductListScreen(productList: List<Product>) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(20.dp)
) {
items(productList.size) { index ->
ProductItem(
product = productList[index],
onProductClick = {
Log.d("ProductListScreen", "Product clicked: ${productList[index].name}")
}
)
}
}
}
@Composable
fun ProductItem(
product: Product,
onProductClick: () -> Unit
) {
var isClicked by remember { mutableStateOf(false) }
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
.clickable {
isClicked = !isClicked // Toggle the clicked state
Log.d("ProductItem", "isClicked toggled to: $isClicked")
onProductClick() // Trigger the passed click action
},
color = if (isClicked) Color.Yellow else Color.LightGray
) {
Row(
modifier = Modifier
.padding(16.dp)
) {
Text(text = product.name)
Text(
text = product.price.toString(),
modifier = Modifier.padding(start = 8.dp)
)
}
}
}
And here's my MainActivity:
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.namecheap_training.model.Product
import com.example.namecheap_training.ui.theme.Namecheap_trainingTheme
import com.example.namecheap_training.ui.theme.ProductListScreen
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
Namecheap_trainingTheme {
ProductListScreen(
listOf(
Product("Test", "123$"),
Product("Test 2", "872$")
)
)
}
}
}
}
Surface has an onClick argument to it, so you can use that instead of appending a clickable modifier.
@ExperimentalMaterialApi
@Composable
fun Surface(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
shape: Shape = RectangleShape,
color: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(color),
border: BorderStroke? = null,
elevation: Dp = 0.dp,
interactionSource: MutableInteractionSource? = null,
content: @Composable () -> Unit
): Unit
Taken from here.
Also, if you look at the release notes, you can see there was a breaking behavior change in Version 1.0.0-beta08 where:
BEHAVIOUR-BREAKING: Surface now consumes clicks, making clicks added via Surface(Modifier.clickable) to be a no-op. Please, use new experimental overload of Surface that accepts onClick. (I73e6c, b/183775620) Added a new Surface overload that handles clicks as well as other clickable functionality: indication, interactionSource, enabled/disabled. It wasn't possible to use a regular non-clickable Surface with the Modifier.clickable because the Surface will not clip the ripple indication in those cases.