placeholder
Compose Modifier
Android
@Composable
public fun Modifier.placeholder(
placeholderState: PlaceholderState,
shape: Shape = PlaceholderDefaults.shape,
color: Color = PlaceholderDefaults.color,
): Modifier
Modifier.placeholder draws a skeleton shape over a component, for situations when no provisional
content (such as cached data) is available. The placeholder skeleton can be displayed instead,
while the content is loading. The reveal of the content will be animated when it becomes
available (and hidden again if the content becomes unavailable), unless the ReducedMotion setting
is enabled, in which case those are instantaneous. NOTE: For animations to work, an AppScaffold
should be used.
If there is some cached data for this field, it may be better to show that while loading, see
Note that the component should still be sized close to the target, so the final reveal of the content is less disruptive.
Parameters
placeholderState | the state used to coordinate several placeholder effects. |
shape | the shape of the placeholder. |
color | the color to use in the placeholder. |
Code Examples
ButtonWithIconAndLabelAndPlaceholders
/**
* This sample applies placeholders directly over the content that is waiting to be loaded. This
* approach is suitable for situations where the developer has an approximate knowledge of how big
* the content is going to be and it doesn't have cached data that can be shown.
*/
@Composable
fun ButtonWithIconAndLabelAndPlaceholders() {
var labelText by remember { mutableStateOf("") }
var imageVector: ImageVector? by remember { mutableStateOf(null) }
val buttonPlaceholderState =
rememberPlaceholderState(isVisible = labelText.isEmpty() || imageVector == null)
FilledTonalButton(
onClick = { /* Do something */ },
enabled = true,
modifier = Modifier.fillMaxWidth().placeholderShimmer(buttonPlaceholderState),
label = {
Text(
text = labelText,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth().placeholder(buttonPlaceholderState),
)
},
icon = {
Box(
modifier =
Modifier.size(ButtonDefaults.IconSize).placeholder(buttonPlaceholderState)
) {
if (imageVector != null) {
Icon(
imageVector = imageVector!!,
contentDescription = "Heart",
modifier =
Modifier.wrapContentSize(align = Alignment.Center)
.size(ButtonDefaults.IconSize)
.fillMaxSize(),
)
}
}
},
)
// Simulate content loading completing in stages
LaunchedEffect(Unit) {
delay(2000)
imageVector = Icons.Filled.Favorite
delay(1000)
labelText = "A label"
}
}
ButtonWithIconAndLabelCachedData
/**
* This sample doesn't use placeholders for the label as there is some cached data that can be shown
* while loading.
*/
@Composable
fun ButtonWithIconAndLabelCachedData() {
var labelText by remember { mutableStateOf("Cached Data") }
var imageVector: ImageVector? by remember { mutableStateOf(null) }
val buttonPlaceholderState =
rememberPlaceholderState(isVisible = labelText.isEmpty() || imageVector == null)
// Put placeholderShimmer in the container and placeholder in the elements of the content that
// have no cached data.
FilledTonalButton(
onClick = { /* Do something */ },
enabled = true,
modifier = Modifier.fillMaxWidth().placeholderShimmer(buttonPlaceholderState),
label = {
Text(
text = labelText,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth(),
)
},
icon = {
Box(
modifier =
Modifier.size(ButtonDefaults.IconSize).placeholder(buttonPlaceholderState)
) {
if (imageVector != null) {
Icon(
imageVector = imageVector!!,
contentDescription = "Heart",
modifier =
Modifier.wrapContentSize(align = Alignment.Center)
.size(ButtonDefaults.IconSize)
.fillMaxSize(),
)
}
}
},
)
// Simulate content loading completing in stages
LaunchedEffect(Unit) {
delay(2000)
imageVector = Icons.Filled.Favorite
delay(1000)
labelText = "A label"
}
}