Package-level declarations
Placeholders display skeleton UI whilst content is loading. They occupy the same space as the real content and cross-fade out once data is ready, reducing perceived wait time.
Use placeholders when:
Data loads asynchronously and the layout shape is known in advance
Multiple items load simultaneously and a consistent loading state is needed
Modifiers
Placeholders are applied as Modifier extensions rather than standalone composables. Each variant targets a specific content type.
Modifier.placeholder
General-purpose skeleton for any layout element. Shape defaults to SparkTheme.shapes.small. Animation is a fade (pulse in/out).
Box(
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
.placeholder(visible = isLoading),
)To override the shape, apply Modifier.clip before this modifier:
Box(
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.placeholder(visible = isLoading),
)Modifier.textPlaceholder
Skeleton for text content. Uses SparkTheme.shapes.full (pill shape) to mirror the rounded profile of text runs. Animation is a fade.
Text(
text = username,
modifier = Modifier.textPlaceholder(visible = isLoading),
)Apply it to every Text composable independently so each line produces its own skeleton at the correct width.
Modifier.illustrationPlaceholder
Skeleton for images and illustrations. Animation is a shimmer (radial gradient sweeping from top-start to bottom-end). Requires an explicit shape parameter.
AsyncImage(
model = url,
contentDescription = null,
modifier = Modifier
.size(128.dp)
.illustrationPlaceholder(
visible = isLoading,
shape = SparkTheme.shapes.extraLarge,
),
)Animation modes
| Mode | Modifier | Behaviour |
|---|---|---|
| Fade | placeholder, textPlaceholder | Colour pulses in and out (600 ms, 200 ms delay, reverses) |
| Shimmer | illustrationPlaceholder | Radial gradient sweeps across the surface (1700 ms, 200 ms delay, restarts) |
All three modifiers apply a cross-fade transition between the skeleton and the real content when visible flips to false.
Full example
@Composable
fun ListingCard(listing: Listing?, isLoading: Boolean) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
AsyncImage(
model = listing?.imageUrl,
contentDescription = null,
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
.illustrationPlaceholder(
visible = isLoading,
shape = SparkTheme.shapes.medium,
),
)
Text(
text = listing?.title.orEmpty(),
modifier = Modifier.textPlaceholder(visible = isLoading),
)
Text(
text = listing?.price.orEmpty(),
modifier = Modifier.textPlaceholder(visible = isLoading),
)
}
}Customising colours
PlaceholderDefaults exposes @Composable functions to produce theme-aware colours for custom highlight implementations:
| Function | Used by | Default alpha |
|---|---|---|
PlaceholderDefaults.color() | Base skeleton fill | 0.25f |
PlaceholderDefaults.fadeHighlightColor() | PlaceholderHighlight.fade() | 0.3f |
PlaceholderDefaults.shimmerHighlightColor() | PlaceholderHighlight.shimmer() | 0.3f |
All three derive from SparkTheme.colors.surface and accept backgroundColor, contentColor, and alpha overrides.
Types
Contains default values used by Modifier.placeholder and PlaceholderHighlight.
A class which provides a brush to paint placeholder based on progress.
Functions
Returns the value used as the color parameter value on Modifier.placeholder.
Creates a PlaceholderHighlight which fades in an appropriate color, using the given animationSpec.
Creates a Fade brush with the given initial and target colors.
Returns the value used as the highlightColor parameter value of PlaceholderHighlight.Companion.fade.
Draws a skeleton UI for illustrations which is typically used whilst a image is 'loading'.
Draws some skeleton UI which is typically used whilst content is 'loading'.
Creates a PlaceholderHighlight which 'shimmers', using a default color.
Creates a PlaceholderHighlight which 'shimmers', using the given highlightColor.
Returns the value used as the highlightColor parameter value of PlaceholderHighlight.Companion.shimmer.
Draws some skeleton UI which is typically used whilst content is 'loading'.