In Jetpack Compose Glimmer, the surface component is a fundamental
building block that represents a distinct visual area or a physical boundary for
components such as buttons and cards.
A surface is responsible for the following visual and physical properties:
- Clipping: Clips its children to a specified shape.
- Border: Draws an inner border to emphasize the component boundary. When focused, it draws a wider border with a focused highlight.
- Background: Applies a background color to the surface area.
- Depth effects: Renders
DepthEffectshadows based on the component's state (such as default versus focused). - Content Color: Provides a color for text and icons inside the surface, calculated by default from the background color.
- Interaction States: Draws a pressed overlay when the surface is pressed and a wider border with a highlight when focused.
Example: Create a surface
The following code creates a surface with clipping, a background, and default borders:
@Composable
fun SurfaceSample() {
Box(Modifier.surface().padding(horizontal = 24.dp, vertical = 20.dp)) {
Text("This is a surface")
}
}
Interaction and focus
Surfaces aren't focusable by default, so users can't interact with them. In most
cases, surfaces should be interactive to let users consistently move focus and
navigate between components. You can use the Compose focusable modifier
for surfaces that are only intended to be focusable. Similarly, you can create a
clickable surface using the Compose clickable modifier. You can also use
other modifiers for surfaces that require actions.
The following code shows examples of both focusable and clickable surfaces:
@Composable fun FocusableSurfaceSample() { val interactionSource = remember { MutableInteractionSource() } Box( modifier = Modifier .size(100.dp) .surface(interactionSource = interactionSource) .focusable(interactionSource = interactionSource), contentAlignment = Alignment.Center ) { Text("Focusable") } } @Composable fun ClickableSurfaceSample() { val interactionSource = remember { MutableInteractionSource() } Box( modifier = Modifier .size(100.dp) .surface(interactionSource = interactionSource) .focusable(interactionSource = interactionSource) .clickable( interactionSource = interactionSource, indication = null ) { /* Handle click action */ }, contentAlignment = Alignment.Center ) { Text("Clickable") } }
Key points about the code
Shared interaction source for focusable surfaces: Both
.surface()and.focusable()must share the sameinteractionSource. This lets the surface react to focus changes.Shared interaction source for clickable surfaces: Both
.surface()and.clickable()must share the sameinteractionSource. This ensures that visual states (like press or focus) are synchronized, letting surface react visually to user input.Modifier ordering: The sequence of modifiers is critical. Because
.surface()clips a layout, placing it before.clickable()ensures the touch target is constrained to the surface's shape. If.clickable()comes first, the interaction area might extend beyond the visible, clipped boundaries of the component.
SurfaceDepthEffect
The SurfaceDepthEffect class manages the transition of shadows between
interaction states:
depthEffect: The shadow effect used when the surface is in its default state.focusedDepthEffect: The shadow effect used when the surface is focused.