Surface
A container that paints color in shape and clips its content to it, with an optional one-dp border and a shadow of elevation.
import com.pantrixui.components.container.surface.PantrixSurface
- Fill.
coloris the surface's colour; left out, it is the theme'ssurface. - On a role's fill. The content is drawn in
onColoron the role'scolor, and inonContaineron itscontainer. - On the grounds. On the theme's
surfaceand itsbackgroundthe surface gives no content colour: each component draws in its own default, a text intextPrimaryand an icon inonSurface. - On a colour the theme does not know. On a fill no role has, the surface gives no colour either; the colour that reads on it is given with
contentColor. - A ground inside a fill. A ground-coloured surface that stands on a fill is a ground again: the colour the fill gave its content ends there.
- Shape.
shapesets the corners; its default is the theme'smdshape. The content is clipped to that shape. - Border. When
borderis given a colour, a 1dp line is drawn around the surface. - Shadow.
elevationputs a shadow under the surface; its default is zero. - The content is in a box.
contentis aBoxScope: the children stand one on top of another and are placed withModifier.align. The surface has no padding of its own.
Examples
At its plainest
The least there is to write: a PantrixSurface with its content. Its colour is the theme's surface and its shape is the theme's md shape; it has no border and no shadow. The surface takes as much room as its content and has no padding of its own: the padding comes from the text inside.
PantrixSurface {
PantrixText(text = "Default surface", modifier = Modifier.padding(PantrixTheme.spacing.lg))
}
On a role's fill
Where color is a role's fill, the surface gives its content the colour paired with that fill: onColor on the role's color, onContainer on its container. The icon and the text in the rows are given no colour; both take what the surface gives.
val primary = PantrixTheme.colors.primary
val success = PantrixTheme.colors.success
PantrixSurface(modifier = Modifier.fillMaxWidth(), color = primary.color) {
SurfaceLine(text = "primary.color: what is inside is drawn in onColor")
}
PantrixSurface(modifier = Modifier.fillMaxWidth(), color = primary.container) {
SurfaceLine(text = "primary.container: what is inside is drawn in onContainer")
}
PantrixSurface(modifier = Modifier.fillMaxWidth(), color = success.container) {
SurfaceLine(text = "success.container: the rule is the same for every role")
}
@Composable
private fun SurfaceLine(text: String) {
Row(
modifier = Modifier.padding(PantrixTheme.spacing.lg),
horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm),
verticalAlignment = Alignment.CenterVertically
) {
PantrixIcon(imageVector = PantrixIcons.Info, contentDescription = null)
PantrixText(text = text)
}
}
On the grounds and on a colour the theme does not know
On the theme's own grounds, surface and background, the surface gives no content colour: each component draws in its own default, a text in textPrimary and an icon in onSurface.
It is the same on a fill no role has: the theme cannot know what reads on that colour. The third surface is painted in the app's own purple, and its content is again drawn in its own defaults. The fourth is given contentColor: on a fill like this it is the caller who says what colour reads on it.
val colors = PantrixTheme.colors
val own = ownColors()
PantrixSurface(modifier = Modifier.fillMaxWidth(), color = colors.surface, border = colors.divider) {
SurfaceLine(text = "surface: no content colour")
}
PantrixSurface(modifier = Modifier.fillMaxWidth(), color = colors.background, border = colors.divider) {
SurfaceLine(text = "background: no content colour")
}
PantrixSurface(modifier = Modifier.fillMaxWidth(), color = own.color) {
SurfaceLine(text = "A colour the theme does not know: no content colour")
}
PantrixSurface(modifier = Modifier.fillMaxWidth(), color = own.color, contentColor = own.onColor) {
SurfaceLine(text = "The same colour: contentColor given")
}
@Composable
private fun SurfaceLine(text: String) {
Row(
modifier = Modifier.padding(PantrixTheme.spacing.lg),
horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm),
verticalAlignment = Alignment.CenterVertically
) {
PantrixIcon(imageVector = PantrixIcons.Info, contentDescription = null)
PantrixText(text = text)
}
}
A ground inside a fill
A ground-coloured surface on a fill is a ground again. The outer surface gives its content onColor; the inner one is the theme's surface and does not pass that colour on: the text inside it is drawn in textPrimary again, and the icon in onSurface. The colour the fill gave would not read on that ground.
PantrixSurface(modifier = Modifier.fillMaxWidth(), color = PantrixTheme.colors.primary.color) {
Column(modifier = Modifier.padding(PantrixTheme.spacing.md)) {
SurfaceLine(text = "On the fill")
PantrixSurface(modifier = Modifier.fillMaxWidth()) {
SurfaceLine(text = "On the inner surface: a ground again")
}
}
}
@Composable
private fun SurfaceLine(text: String) {
Row(
modifier = Modifier.padding(PantrixTheme.spacing.lg),
horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm),
verticalAlignment = Alignment.CenterVertically
) {
PantrixIcon(imageVector = PantrixIcons.Info, contentDescription = null)
PantrixText(text = text)
}
}
Reading the content colour
The content colour is carried by LocalPantrixContentColor; something you draw yourself can read it too. The dot here is a Box, and takes its colour from there. On the grounds the value is Color.Unspecified: there the dot picks its own default.
val danger = PantrixTheme.colors.danger
Row(horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.md)) {
PantrixSurface(color = danger.color) { SurfaceDot() }
PantrixSurface(color = danger.container) { SurfaceDot() }
PantrixSurface(border = PantrixTheme.colors.divider) { SurfaceDot() }
}
@Composable
private fun SurfaceDot() {
val color = LocalPantrixContentColor.current.takeOrElse { PantrixTheme.colors.onSurface }
Box(modifier = Modifier.padding(PantrixTheme.spacing.lg).size(16.dp).background(color, CircleShape))
}
Shape and clipping
shape is the surface's corners: one of the theme's shapes, or any Shape. The square surfaces show five of the theme's shapes; the default is md.
The content is clipped to the surface's shape. The band inside the bottom surface is a rectangle from edge to edge; its corners were cut by the surface's xl shape.
val shapes = PantrixTheme.shapes
val info = PantrixTheme.colors.info
FlowRow(
horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.md),
verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.md)
) {
listOf("none" to shapes.none, "sm" to shapes.sm, "md" to shapes.md, "xl" to shapes.xl, "full" to shapes.full)
.forEach { (name, shape) ->
PantrixSurface(modifier = Modifier.size(72.dp), color = info.container, shape = shape) {
PantrixText(text = name, modifier = Modifier.align(Alignment.Center))
}
}
}
PantrixSurface(
modifier = Modifier.widthIn(max = 360.dp),
shape = shapes.xl,
border = PantrixTheme.colors.divider
) {
Column {
Box(modifier = Modifier.fillMaxWidth().height(64.dp).background(info.color))
PantrixText(
text = "The surface's shape cut the band's corners",
modifier = Modifier.padding(PantrixTheme.spacing.lg)
)
}
}
Border
When border is given a colour, a 1dp line that follows the surface's shape is drawn around it; without one there is no border. A panel's border is usually the theme's divider colour. The thickness does not change: only the colour is given.
val colors = PantrixTheme.colors
PantrixSurface(modifier = Modifier.fillMaxWidth(), shape = PantrixTheme.shapes.lg, border = colors.divider) {
PantrixText(text = "A border in the divider colour", modifier = Modifier.padding(PantrixTheme.spacing.lg))
}
PantrixSurface(
modifier = Modifier.fillMaxWidth(),
color = colors.primary.container,
shape = PantrixTheme.shapes.lg,
border = colors.primary.color
) {
PantrixText(text = "A fill, and a border in the role's colour", modifier = Modifier.padding(PantrixTheme.spacing.lg))
}
Shadow
elevation puts a shadow under the surface, following its shape. Here are the five steps of the theme's elevation scale: PantrixTheme.elevation, from 0dp to 8dp. The shadow is drawn outside the surface's bounds: it needs room around the surface.
val elevation = PantrixTheme.elevation
FlowRow(
horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.xl),
verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.xl)
) {
listOf(
"none" to elevation.none,
"sm" to elevation.sm,
"md" to elevation.md,
"lg" to elevation.lg,
"xl" to elevation.xl
).forEach { (name, depth) ->
PantrixSurface(modifier = Modifier.size(72.dp), elevation = depth) {
PantrixText(text = name, modifier = Modifier.align(Alignment.Center))
}
}
}
The content is in a box
content is a BoxScope: the children are not laid out one under another, they stand one on top of another, and each picks its place with Modifier.align. To lay them out one under another, put a Column inside.
val spacing = PantrixTheme.spacing
PantrixSurface(
modifier = Modifier.fillMaxWidth().height(120.dp),
color = PantrixTheme.colors.neutral.container
) {
PantrixText(text = "TopStart", modifier = Modifier.align(Alignment.TopStart).padding(spacing.md))
PantrixText(text = "Center", modifier = Modifier.align(Alignment.Center))
PantrixText(text = "BottomEnd", modifier = Modifier.align(Alignment.BottomEnd).padding(spacing.md))
}
API
modifier
A container that paints color in shape and clips its content to it, with an optional one-dp border and a shadow of elevation.
| Parameter | Type | Default | What it is |
|---|---|---|---|
modifier | Modifier | Modifier | the surface's modifier: its size and its place. |
color | Color | PantrixTheme.colors.surface | |
contentColor | Color | Color.Unspecified | |
shape | Shape | PantrixTheme.shapes.md | |
border | Color | Color.Unspecified | |
elevation | Dp | 0.dp | |
content | @Composable BoxScope.() -> Unit | what the surface holds, drawn in its content colour and clipped to shape. |