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 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 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 the highlightColor parameter value of PlaceholderHighlight.Companion.shimmer.
Draws some skeleton UI which is typically used whilst content is 'loading'.