minimumInteractiveComponentSize

Functions summary

Modifier

Reserves at least 48.dp in size to disambiguate touch interactions if the element would measure smaller.

Cmn

Functions

Modifier.minimumInteractiveComponentSize

fun Modifier.minimumInteractiveComponentSize(): Modifier

Reserves at least 48.dp in size to disambiguate touch interactions if the element would measure smaller.

Target sizes

This uses the Material recommended minimum size of 48.dp x 48.dp, which may not the same as the system enforced minimum size. The minimum clickable / touch target size (48.dp by default) is controlled by the system via ViewConfiguration and automatically expanded at the touch input layer.

This modifier is not needed for touch target expansion to happen. It only affects layout, to make sure there is adequate space for touch target expansion.

Because layout constraints are affected by modifier order, for this modifier to take effect, it must come before any size modifiers on the element that might limit its constraints.

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.size
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Composable
fun Widget(color: Color, modifier: Modifier = Modifier) {
    // Default size is 24.dp, which is smaller than the recommended touch target
    Box(modifier.size(24.dp).background(color))
}

Column(Modifier.border(1.dp, Color.Black)) {
    // Not interactable, no need for touch target enforcement
    Widget(Color.Red)

    Widget(
        color = Color.Green,
        modifier =
            Modifier.clickable { /* do something */ }
                // Component is now interactable, so it should enforce a sufficient touch target
                .minimumInteractiveComponentSize(),
    )

    Widget(
        color = Color.Blue,
        modifier =
            Modifier.clickable { /* do something */ }
                // Component is now interactable, so it should enforce a sufficient touch target
                .minimumInteractiveComponentSize()
                // Any size modifiers should come after `minimumInteractiveComponentSize`
                // so as not to interfere with layout expansion
                .size(36.dp),
    )
}
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.selection.toggleable
import androidx.compose.material3.Checkbox
import androidx.compose.material3.Text
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp

var checked by remember { mutableStateOf(false) }

// The entire row accepts interactions to toggle the checkbox,
// so we apply `minimumInteractiveComponentSize`
Row(
    verticalAlignment = Alignment.CenterVertically,
    modifier =
        Modifier.toggleable(
                value = checked,
                onValueChange = { checked = it },
                role = Role.Checkbox,
            )
            .minimumInteractiveComponentSize(),
) {
    // Cannot rely on Checkbox for touch target expansion because it only enforces
    // `minimumInteractiveComponentSize` if onCheckedChange is non-null
    Checkbox(checked = checked, onCheckedChange = null)
    Spacer(Modifier.width(8.dp))
    Text("Label for checkbox")
}