# ripple

> Creates a Ripple using the provided values and values inferred from the [MaterialTheme].

## Code Examples

### EnableInsetFocusRingSample

```kotlin
@Sampled
@Composable
fun EnableInsetFocusRingSample() {
    // Enable inset focus rings through the theme configuration.
    CompositionLocalProvider(
        LocalRippleThemeConfiguration provides RippleDefaults.InsetFocusRingThemeConfiguration
    ) {
        // Button is a standard Wear Material3 component that supports focus rings internally.
        Button(onClick = { /* Do something */ }, label = { Text("Button") })
    }
}
```

### CustomizeInsetFocusRingSample

```kotlin
@Sampled
@Composable
fun CustomizeInsetFocusRingSample() {
    // Customize the focus ring stroke sizes through the theme configuration.
    CompositionLocalProvider(
        LocalRippleThemeConfiguration provides
            RippleThemeConfiguration(
                RippleThemeConfiguration.Focus.InsetRing(
                    outerStrokeInset = 0.dp,
                    outerStrokeWidth = 3.dp,
                    innerStrokeInset = 0.dp,
                    innerStrokeWidth = 6.dp,
                )
            )
    ) {
        Column(
            modifier = Modifier.fillMaxSize(),
            verticalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterVertically),
            horizontalAlignment = Alignment.CenterHorizontally,
        ) {
            // Button is a standard Wear Material3 component that supports focus rings internally.
            Button(onClick = { /* Do something */ }, label = { Text("Button") })

            // SwitchButton is also a standard component that supports focus rings internally.
            // We override its focus ring colors here.
            CompositionLocalProvider(
                LocalRippleConfiguration provides
                    RippleConfiguration(
                        focus =
                            RippleConfiguration.Focus.InsetRing(
                                outerStrokeColor = MaterialTheme.colorScheme.tertiary,
                                innerStrokeColor = MaterialTheme.colorScheme.onTertiary,
                            )
                    )
            ) {
                SwitchButton(
                    checked = true,
                    onCheckedChange = { /* Do something */ },
                    label = { Text("SwitchButton") },
                )
            }
        }
    }
}
```

### ClickableWithFocusRingShapeSample

```kotlin
@Sampled
@Composable
fun ClickableWithFocusRingShapeSample() {
    CompositionLocalProvider(
        LocalRippleThemeConfiguration provides RippleDefaults.InsetFocusRingThemeConfiguration
    ) {
        // Pass ripple() explicitly to use the same shape for the component and its focus ring.
        val shape = MaterialTheme.shapes.large
        Box(
            modifier =
                Modifier.fillMaxWidth()
                    .clip(shape)
                    .background(MaterialTheme.colorScheme.surfaceContainer)
                    .clickable(
                        interactionSource = null,
                        indication = ripple(focusRingShape = shape),
                        onClick = { /* Do something */ },
                    )
                    .padding(12.dp),
            contentAlignment = Alignment.Center,
        ) {
            Text("Clickable box")
        }
    }
}
```

### FocusableWithFocusRingShapeSample

```kotlin
@Sampled
@Composable
fun FocusableWithFocusRingShapeSample() {
    CompositionLocalProvider(
        LocalRippleThemeConfiguration provides RippleDefaults.InsetFocusRingThemeConfiguration
    ) {
        val shape = MaterialTheme.shapes.large
        val interactionSource = remember { MutableInteractionSource() }
        // indication draws but does not emit interactions; focusable (or clickable) emits them.
        Box(
            modifier =
                Modifier.fillMaxWidth()
                    .clip(shape)
                    .background(MaterialTheme.colorScheme.surfaceContainer)
                    .indication(interactionSource, ripple(focusRingShape = shape))
                    .focusable(interactionSource = interactionSource)
                    .padding(12.dp),
            contentAlignment = Alignment.Center,
        ) {
            Text("Focusable box")
        }
    }
}
```

### BorderedClickableWithFocusRingShapeSample

```kotlin
@Sampled
@Composable
fun BorderedClickableWithFocusRingShapeSample() {
    CompositionLocalProvider(
        LocalRippleThemeConfiguration provides RippleDefaults.InsetFocusRingThemeConfiguration
    ) {
        val shape = MaterialTheme.shapes.large
        val hasInsetFocusRing =
            LocalRippleThemeConfiguration.current.focus is RippleThemeConfiguration.Focus.InsetRing
        val interactionSource =
            if (hasInsetFocusRing) remember { MutableInteractionSource() } else null
        Box(
            modifier =
                Modifier.fillMaxWidth()
                    // Draw the focus ring before the border, so that it is drawn on top of it.
                    .then(
                        if (interactionSource != null) {
                            Modifier.indication(
                                interactionSource = interactionSource,
                                indication =
                                    ripple(
                                        focusRingShape = shape,
                                        enablePressIndication = false,
                                        enableHoverIndication = false,
                                        enableDragIndication = false,
                                    ),
                            )
                        } else {
                            Modifier
                        }
                    )
                    .border(2.dp, MaterialTheme.colorScheme.outline, shape)
                    .clip(shape)
                    // Pass ripple() explicitly to skip focus indication; the ring is drawn above.
                    .clickable(
                        interactionSource = interactionSource,
                        indication = ripple(enableFocusIndication = !hasInsetFocusRing),
                        onClick = { /* Do something */ },
                    )
                    .padding(12.dp),
            contentAlignment = Alignment.Center,
        ) {
            Text("Bordered clickable box")
        }
    }
}
```

### ClickableWithDynamicFocusRingShapeSample

```kotlin
@Sampled
@Composable
fun ClickableWithDynamicFocusRingShapeSample() {
    CompositionLocalProvider(
        LocalRippleThemeConfiguration provides RippleDefaults.InsetFocusRingThemeConfiguration
    ) {
        var isSelected by remember { mutableStateOf(false) }
        val primaryColor = MaterialTheme.colorScheme.primary
        val tertiaryColor = MaterialTheme.colorScheme.tertiary
        // ColorProducer reads state during draw, avoiding recomposition when the ripple color
        // changes, and preserving the existing ripple state instead of recreating the ripple.
        val colorProducer =
            remember(primaryColor, tertiaryColor) {
                ColorProducer { if (isSelected) primaryColor else tertiaryColor }
            }
        val shape = if (isSelected) MaterialTheme.shapes.large else MaterialTheme.shapes.small
        // Delegate from a single Shape instance to the changing shape to preserve the ripple node.
        val focusRingShape = remember {
            object : Shape {
                var currentShape: Shape = shape

                override fun createOutline(
                    size: Size,
                    layoutDirection: LayoutDirection,
                    density: Density,
                ): Outline = currentShape.createOutline(size, layoutDirection, density)
            }
        }
        focusRingShape.currentShape = shape
        Box(
            modifier =
                Modifier.fillMaxWidth()
                    .clip(shape)
                    .background(MaterialTheme.colorScheme.surfaceContainer)
                    .clickable(
                        interactionSource = null,
                        indication = ripple(color = colorProducer, focusRingShape = focusRingShape),
                        onClick = { isSelected = !isSelected },
                    )
                    .padding(12.dp),
            contentAlignment = Alignment.Center,
        ) {
            Text(if (isSelected) "Selected" else "Unselected")
        }
    }
}
```
