Ever run into weird layout bugs or mysteriously unclickable components in Jetpack Compose? The culprit might be how you're stacking your modifiers β€” especially when you're conditionally adding them. Let's fix that, once and for all.

πŸ’‘ Use Case: Conditionally Clickable Cards

Imagine you're building a PermissionCard composable like this one above in the pic:

@Composable
fun PermissionCard(
    icon: ImageVector,
    title: String,
    description: String,
    showSwitch: Boolean = true,
    switchChecked: Boolean = false,
    onSwitchToggle: ((Boolean) -> Unit)? = null,
    modifier: Modifier = Modifier,
    onClick: (() -> Unit)? = null
) {
    Surface(
        shape = MaterialTheme.shapes.medium,
        color = colorResource(id = R.color.carraraColor),
        modifier = modifier
            .then(
                if (onClick != null) {
                    Modifier.clickable { onClick() }
                } else {
                    Modifier
                }
            )
            .fillMaxWidth()
            .padding(all = basePadding)
    ) {
   ........ more code

βœ… Why this is the right way:

  • πŸ”— Modifiers are immutable Each .padding(), .clickable(), etc., returns a new Modifier. You're building a chain, not mutating a single object.
  • 🧱 then() stacks modifiers safely Using .then(...) lets you conditionally add a modifier without restarting or overriding the modifier chain.
  • πŸ‘‡ Conditional logic stays clean Avoids deeply nested if statements and keeps your modifier logic declarative.

🚫 What not to do:

.then(
    if (onClick != null) modifier.clickable { onClick() } else modifier
)
  • ❌ You're reusing modifier inside itself
  • ❌ You might duplicate layout modifiers
  • ❌ Leads to unexpected or buggy behavior

πŸ§ͺ Real Example: Privacy Policy Card

This PermissionCard gets reused for various settings β€” like navigating to my Privacy Policy:

@Composable
fun PrivacyPolicyCard(navController: NavController) {
    val encodedUrl = URLEncoder.encode("https://...", StandardCharsets.UTF_8)
    PermissionCard(
        icon = Icons.Default.Lock,
        title = "Privacy Policy",
        description = "Read our policy to understand\nhow we use your data.",
        showSwitch = false,
        onClick = { navController.navigate(...createRoute(encodedUrl)) }
    )
}

By using .then(...), my PermissionCard only becomes clickable when onClick is passed, without affecting layout or requiring awkward branching logic.

✍️ Final Thoughts

When building reusable components:

  • Use then(...) for conditional modifiers.
  • Always pass a fresh Modifier, not the one you're building.
  • Keep layout and interaction modifiers cleanly separated.

πŸ‘‰ Use Modifier.then() the smart way β€” and never wonder why your clicks stopped working again.

πŸ“± Like What You're Seeing?

If you've enjoyed this dive into Modifier.then() and how I'm building clean, reusable UI in Jetpack Compose...

πŸ‘‰ Check out what I'm building on Google Play!

It's a sports-focused app built entirely with Jetpack Compose β€” including components like the PermissionCard you saw in this post. Your support means a lot, so leave a review if you have a moment β€” I'd love your feedback!

πŸ—£οΈ: reach out on LinkedIn, X or Insta

Best, Rafa