placeholderShimmer

Composable Function
Android
@Composable
public fun Modifier.placeholderShimmer(
    placeholderState: PlaceholderState,
    shape: Shape = PlaceholderDefaults.shape,
    color: Color = PlaceholderDefaults.shimmerColor,
): Modifier

Modifier.placeholderShimmer draws a periodic shimmer over content, indicating to the user that contents are loading or potentially out of date. The placeholder shimmer is a 45 degree gradient from Top|Left of the screen to Bottom|Right. The shimmer is coordinated via the animation frame clock which orchestrates the shimmer so that every component will shimmer as the gradient progresses across the screen. NOTE: For animations to work, an AppScaffold should be used.

Example of a Button with icon and a label that put placeholders over individual content slots and then draws a placeholder shimmer over the result:

Example of a simple text placeholder:

Parameters

placeholderState the current placeholder state that determine whether the placeholder shimmer should be shown.
shape the shape of the component.
color the color to use in the shimmer.

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"
    }
}

TextPlaceholder

/**
 * This sample applies a placeholder and placeholderShimmer directly over a single composable.
 *
 * Note that the modifier ordering is important, the placeholderShimmer must be before the
 * placeholder in the modifier chain - otherwise the shimmer will be drawn underneath the
 * placeholder and will not be visible.
 */
@Composable
fun TextPlaceholder() {
    var labelText by remember { mutableStateOf("") }
    val placeholderState = rememberPlaceholderState(isVisible = labelText.isEmpty())
    Text(
        text = labelText,
        overflow = TextOverflow.Ellipsis,
        textAlign = TextAlign.Center,
        modifier =
            Modifier.width(90.dp).placeholderShimmer(placeholderState).placeholder(placeholderState),
    )
    // Simulate content loading
    LaunchedEffect(Unit) {
        delay(3000)
        labelText = "A label"
    }
}