The focusRing is based on the outline modifier and does not affect layout or size - it draws purely outside the composable's bounds. By default it follows focus-visible behavior: keyboard focus shows the ring, while pointer-origin focus does not.
API Reference
FocusVisibilityProvider
| Parameter | Type | Description |
|---|---|---|
modifier |
Modifier |
Modifier to be applied to the provider container. |
content |
() -> Unit |
The content that should share focus-visible input tracking. |
Modifier.focusRing
| Parameter | Type | Description |
|---|---|---|
interactionSource |
InteractionSource |
The InteractionSource to subscribe to for focus events |
width |
Dp |
The thickness of the focus ring |
color |
Color |
The color of the focus ring |
shape |
Shape |
The shape of the composable (defaults to RectangleShape) |
offset |
Dp |
Distance between composable and ring (defaults to 0.dp) |
visibility |
FocusRingVisibility |
Controls whether the ring appears for any focus or only focus-visible focus (defaults to FocusRingVisibility.FocusVisible) |
Installation
implementation("com.composables:composeunstyled-focus-ring")
Code Examples
Basic Example
Focus ring requires an interactionSource, width and color to render a focus indicator around the component.
The shape parameter should match the shape of the composable you are styling for proper alignment.
val interactionSource = remember { MutableInteractionSource() }
FocusVisibilityProvider {
SimpleButton(
modifier = Modifier.focusRing(
interactionSource = interactionSource,
width = 2.dp,
color = Color(0xFF3B82F6),
shape = RoundedCornerShape(8.dp),
offset = 2.dp
),
interactionSource = interactionSource
)
}
Focus Visibility
Wrap your app or screen content with FocusVisibilityProvider so Compose Unstyled can track whether the latest input came from the keyboard or a pointer. Programmatic focus and unknown input modes are treated conservatively and show the ring.
Use visibility = FocusRingVisibility.Focused when you need the old behavior where any focused component shows the ring.
FocusVisibilityProvider {
SimpleButton(
modifier = Modifier.focusRing(
interactionSource = interactionSource,
width = 2.dp,
color = Color(0xFF3B82F6),
visibility = FocusRingVisibility.Focused
),
interactionSource = interactionSource
)
}
Customizing Width
You can customize the thickness of the focus ring by adjusting the width parameter.
val interactionSource = remember { MutableInteractionSource() }
SimpleButton(
modifier = Modifier.focusRing(interactionSource, 1.dp, Color(0xFF3B82F6), shape = RoundedCornerShape(8.dp)),
interactionSource = interactionSource
)
SimpleButton(
modifier = Modifier.focusRing(interactionSource, 2.dp, Color(0xFF3B82F6), shape = RoundedCornerShape(8.dp)),
interactionSource = interactionSource
)
SimpleButton(
modifier = Modifier.focusRing(interactionSource, 4.dp, Color(0xFF3B82F6), shape = RoundedCornerShape(8.dp)),
interactionSource = interactionSource
)
Customizing Shape
The focus ring adapts to different shapes. The shape parameter should match the shape of your composable for proper alignment.
Note that generic shapes are not supported and will fail silently.
val interactionSource = remember { MutableInteractionSource() }
SimpleButton(
shape = RectangleShape,
modifier = Modifier.focusRing(interactionSource, 2.dp, Color(0xFF3B82F6), shape = RectangleShape),
interactionSource = interactionSource
)
SimpleButton(
shape = RoundedCornerShape(8.dp),
modifier = Modifier.focusRing(interactionSource, 2.dp, Color(0xFF3B82F6), shape = RoundedCornerShape(8.dp)),
interactionSource = interactionSource
)
SimpleButton(
shape = RoundedCornerShape(100),
modifier = Modifier.focusRing(interactionSource, 2.dp, Color(0xFF3B82F6), shape = RoundedCornerShape(100)),
interactionSource = interactionSource
)
Customizing Offset
The offset parameter controls the distance between the composable and its focus ring, creating a gap effect.
val interactionSource = remember { MutableInteractionSource() }
SimpleButton(
modifier = Modifier.focusRing(interactionSource, 2.dp, Color(0xFF3B82F6), offset = 0.dp, shape = RoundedCornerShape(8.dp)),
interactionSource = interactionSource
)
SimpleButton(
modifier = Modifier.focusRing(interactionSource, 2.dp, Color(0xFF3B82F6), offset = 4.dp, shape = RoundedCornerShape(8.dp)),
interactionSource = interactionSource
)
SimpleButton(
modifier = Modifier.focusRing(interactionSource, 2.dp, Color(0xFF3B82F6), offset = 8.dp, shape = RoundedCornerShape(8.dp)),
interactionSource = interactionSource
)
Customizing Color
You can customize the focus ring color to match your design system or create visual emphasis.
val redInteractionSource = remember { MutableInteractionSource() }
val greenInteractionSource = remember { MutableInteractionSource() }
val purpleInteractionSource = remember { MutableInteractionSource() }
SimpleButton(
modifier = Modifier.focusRing(redInteractionSource, 2.dp, Color(0xFFEF4444), offset = 2.dp, shape = RoundedCornerShape(8.dp)),
interactionSource = redInteractionSource
)
SimpleButton(
modifier = Modifier.focusRing(greenInteractionSource, 2.dp, Color(0xFF10B981), offset = 2.dp, shape = RoundedCornerShape(8.dp)),
interactionSource = greenInteractionSource
)
SimpleButton(
modifier = Modifier.focusRing(purpleInteractionSource, 2.dp, Color(0xFF8B5CF6), offset = 2.dp, shape = RoundedCornerShape(8.dp)),
interactionSource = purpleInteractionSource
)