Manuel Lucas
Manuel Lucas

Reputation: 542

Clickable modifier doesn't work with Card in jetpack compose

I was developing some app in jetpack compose, and I get into the issue of trying to add an onClick event to some custom component and doesn't works, due to the onClickk method is not been called.

Here the way I'm try to add the on click:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DashboardScreen(
    context: Context,
    navController: NavController,
    dashboardViewModel: DashboardViewModel,
    authViewModel: AuthViewModel,
) {

    val list = listOf(
        DashboardItem(R.drawable.baseline_payment_24, context.getString(R.string.sellTitle)),
        DashboardItem(R.drawable.baseline_credit_score_24, context.getString(R.string.preCheck)),
        DashboardItem(R.drawable.baseline_money_24, context.getString(R.string.price)),
        DashboardItem(R.drawable.twotone_list_alt_24, context.getString(R.string.list))
    )

... some code

Theme {
Column(
   verticalArrangement = Arrangement.Center,
   modifier = Modifier.padding(top = 16.dp)
            ) {
                LazyVerticalGrid(
                    modifier = Modifier
                        .fillMaxSize()
                        .padding(end = 16.dp),
                    columns = GridCells.Fixed(2),
                    verticalArrangement = Arrangement.Top,
                    horizontalArrangement = Arrangement.Center
                ) {
                    items(list.size) { index ->
                        DashboardComponent(
                            item = list[index],
                            modifier = Modifier
                                .padding(paddingValues)
                                .background(Color.White)
                                .clickable {
                                    setOnClick(
                                        list,
                                        index,
                                        context,
                                        dashboardViewModel,
                                        navController
                                    )
                                }
                        )
                    }
                }
            }

private fun setOnClick(
    list: List<DashboardItem>,
    index: Int,
    context: Context,
    dashboardViewModel: DashboardViewModel,
    navController: NavController,
) {
    when (list[index].title) {
        context.getString(R.string.sellTitle) -> {
            dashboardViewModel.setIsPreCheck(false)
            navController.navigate(Screens.Sell.route)
        }

        context.getString(R.string.preCheck) -> {
            dashboardViewModel.setIsPreCheck(true)
            navController.navigate(Screens.Sell.route)
        }

        context.getString(R.string.list) -> {
            navController.navigate(Screens.TransactionList.route)
        }

        context.getString(R.string.price) -> {
            navController.navigate(Screens.ListProducts.route)
        }
    }
}

here is my UI which do nothing:

enter image description here

I've found the onClick event is not been called when I try to debug the app, so I guess this solution doesn't work.

Do you know some way to add on click into a list of components which actually works.

Thanks in advance !

[EDIT]

Add DashboardComponent.kt

@Composable
fun DashboardComponent(
    modifier: Modifier,
    item: DashboardItem,
) {

    CepsaStandaloneAppTheme {
        Card(
            modifier = modifier
                .size((LocalConfiguration.current.screenWidthDp.dp)/3),
            colors = CardDefaults.cardColors(
                containerColor = Color.White
            ),
            elevation = CardDefaults.cardElevation(8.dp),
            shape = RoundedCornerShape(10),
        ) {
            Row(
                modifier = Modifier.fillMaxSize(),
                horizontalArrangement = Arrangement.Center,
            ) {
                Column(
                    verticalArrangement = Arrangement.Center,
                    horizontalAlignment = Alignment.CenterHorizontally,
                    modifier = Modifier.fillMaxSize(),
                ) {
                    Icon(
                        painter = painterResource(id = item.icon),
                        contentDescription = "icon",
                        tint = PrimaryColor,
                        modifier = Modifier.size(50.dp)
                    )
                    Text(
                        text = item.title,
                        color = PrimaryColor,
                        fontSize = 24.sp
                    )
                }
            }
        }
    }
}

[Solution]

Finally I get into a working solution adding a Box around the content of my Card in DashboardComponent, and passing the onClick as argument:

DashboardComponent.kt

@Composable
fun DashboardComponent(
    modifier: Modifier,
    item: DashboardItem,
    onClick: () -> Unit
) {

    CepsaStandaloneAppTheme {
        Card(
            modifier = modifier
                .size((LocalConfiguration.current.screenWidthDp.dp)/3),
            colors = CardDefaults.cardColors(
                containerColor = Color.White
            ),
            elevation = CardDefaults.cardElevation(8.dp),
            shape = RoundedCornerShape(10),
        ) {
            Box(modifier = Modifier
                .fillMaxSize()
                .clickable { onClick.invoke() }) {
                Row(
                    modifier = Modifier.fillMaxSize(),
                    horizontalArrangement = Arrangement.Center,
                ) {
                    Column(
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally,
                        modifier = Modifier.fillMaxSize(),
                    ) {
                        Icon(
                            painter = painterResource(id = item.icon),
                            contentDescription = "icon",
                            tint = PrimaryColor,
                            modifier = Modifier.size(50.dp)
                        )
                        Text(
                            text = item.title,
                            color = PrimaryColor,
                            fontSize = 24.sp
                        )
                    }
                }
            }
        }
    }
}

Upvotes: 3

Views: 4087

Answers (1)

Jan B&#237;na
Jan B&#237;na

Reputation: 7278

The problem is that the clickable modifier is applied to Card. Card delegates to Surface and Surface effectively disables clickable modifier by applying Modifier.pointerInput(Unit) {} at the end of the modifier chain. You can see that here: Surface.kt
Why? Because applying the clickable modifier yourself wouldn't work as expected - Card/Surface has a Shape that won't be applied to the clickable modifier, so the ripple effect (indication) wouldn't respect rounded corners and it will look ugly. Because of that, clickable modifier is disabled and there are overloads for Card and Surface that have onClick argument - you should use these overloads.
So instead of this:

Card(modifier = Modifier.clickable {})

you do this:

Card(onClick = {})

Upvotes: 11

Related Questions