New Compose Multiplatform components arrived on Composables UICheck it out →

Modifier in Wear Material Compose

placeholder

Android

Draws a placeholder shape over the top of a composable and animates a wipe off effect to remove the placeholder. Typically used whilst content is 'loading' and then 'revealed'.

Last updated:

Installation

dependencies {
   implementation("androidx.wear.compose:compose-material:1.4.0-beta03")
}

Overloads

@Composable
@ExperimentalWearMaterialApi
@Suppress("ComposableModifierFactory", "DEPRECATION"
fun Modifier.placeholder(
    placeholderState: PlaceholderState,
    shape: Shape = MaterialTheme.shapes.small,
    color: Color =
        MaterialTheme.colors.onSurface.copy(alpha = 0.1f)
            .compositeOver(MaterialTheme.colors.surface)
)

Parameters

namedescription
placeholderStatedetermines whether the placeholder is visible and controls animation effects for the placeholder.
shapethe shape to apply to the placeholder
colorthe color of the placeholder.

Code Examples

ChipWithIconAndLabelAndPlaceholders

@Composable
@Sampled
@OptIn(ExperimentalWearMaterialApi::class
fun ChipWithIconAndLabelAndPlaceholders() {
    var labelText by remember { mutableStateOf("") }
    var iconResource: Int? by remember { mutableStateOf(null) }
    val chipPlaceholderState = rememberPlaceholderState {
        labelText.isNotEmpty() && iconResource != null
    }

    Chip(
        onClick = { /* Do something */ },
        enabled = true,
        label = {
            Text(
                text = labelText,
                maxLines = 2,
                overflow = TextOverflow.Ellipsis,
                modifier = Modifier
                    .fillMaxWidth()
                    .placeholder(chipPlaceholderState)
            )
        },
        icon = {
            Box(
                modifier = Modifier
                    .size(ChipDefaults.IconSize)
                    .placeholder(chipPlaceholderState)
            ) {
                if (iconResource != null) {
                    Icon(
                        painter = painterResource(id = R.drawable.ic_airplanemode_active_24px),
                        contentDescription = "airplane",
                        modifier = Modifier
                            .wrapContentSize(align = Alignment.Center)
                            .size(ChipDefaults.IconSize)
                            .fillMaxSize(),
                    )
                }
            }
        },
        colors = PlaceholderDefaults.placeholderChipColors(
            originalChipColors = ChipDefaults.primaryChipColors(),
            placeholderState = chipPlaceholderState
        ),
        modifier = Modifier
            .fillMaxWidth()
            .placeholderShimmer(chipPlaceholderState)
    )
    // Simulate content loading completing in stages
    LaunchedEffect(Unit) {
        delay(2000)
        iconResource = R.drawable.ic_airplanemode_active_24px
        delay(1000)
        labelText = "A label"
    }
    if (! chipPlaceholderState.isShowContent) {
        LaunchedEffect(chipPlaceholderState) {
            chipPlaceholderState.startPlaceholderAnimation()
        }
    }
}

ChipWithIconAndLabelsAndOverlaidPlaceholder

@Composable
@Sampled
@OptIn(ExperimentalWearMaterialApi::class
fun ChipWithIconAndLabelsAndOverlaidPlaceholder() {
    var labelText by remember { mutableStateOf("") }
    var secondaryLabelText by remember { mutableStateOf("") }
    var iconResource: Int? by remember { mutableStateOf(null) }

    val chipPlaceholderState = rememberPlaceholderState {
        labelText.isNotEmpty() && secondaryLabelText.isNotEmpty() && iconResource != null
    }
    Box {
        if (chipPlaceholderState.isShowContent ||
            chipPlaceholderState.isWipeOff
        ) {
            Chip(
                onClick = { /* Do something */ },
                enabled = true,
                label = {
                    Text(
                        text = labelText,
                        maxLines = 1,
                        overflow = TextOverflow.Ellipsis,
                        modifier = Modifier
                            .fillMaxWidth()
                    )
                },
                secondaryLabel = {
                    Text(
                        text = secondaryLabelText,
                        maxLines = 1,
                        overflow = TextOverflow.Ellipsis,
                        modifier = Modifier
                            .fillMaxWidth()
                    )
                },
                icon = {
                    if (iconResource != null) {
                        Icon(
                            painter = painterResource(
                                id = R.drawable.ic_airplanemode_active_24px
                            ),
                            contentDescription = "airplane",
                            modifier = Modifier
                                .wrapContentSize(align = Alignment.Center)
                                .size(ChipDefaults.IconSize)
                        )
                    }
                },
                colors = ChipDefaults.gradientBackgroundChipColors(),
                modifier = Modifier.fillMaxWidth()
            )
        }
        if (! chipPlaceholderState.isShowContent) {
            Chip(
                onClick = { /* Do something */ },
                enabled = true,
                label = {
                    Box(
                        modifier = Modifier
                            .fillMaxWidth()
                            .height(16.dp)
                            .padding(top = 1.dp, bottom = 1.dp)
                            .placeholder(placeholderState = chipPlaceholderState)
                    )
                },
                secondaryLabel = {
                    Box(
                        modifier = Modifier
                            .fillMaxWidth()
                            .height(16.dp)
                            .padding(top = 1.dp, bottom = 1.dp)
                            .placeholder(placeholderState = chipPlaceholderState)
                    )
                },
                icon = {
                    Box(
                        modifier = Modifier
                            .size(ChipDefaults.IconSize)
                            .placeholder(chipPlaceholderState)
                    )
                    // Simulate the icon becoming ready after a period of time
                    LaunchedEffect(Unit) {
                        delay(2000)
                        iconResource = R.drawable.ic_airplanemode_active_24px
                    }
                },
                colors = PlaceholderDefaults.placeholderChipColors(
                    placeholderState = chipPlaceholderState
                ),
                modifier = Modifier
                    .fillMaxWidth()
                    .placeholderShimmer(chipPlaceholderState)
            )
        }
    }
    // Simulate data being loaded after a delay
    LaunchedEffect(Unit) {
        delay(2500)
        secondaryLabelText = "A secondary label"
        delay(500)
        labelText = "A label"
    }
    if (! chipPlaceholderState.isShowContent) {
        LaunchedEffect(chipPlaceholderState) {
            chipPlaceholderState.startPlaceholderAnimation()
        }
    }
}

TextPlaceholder

@Composable
@Sampled
@OptIn(ExperimentalWearMaterialApi::class
fun TextPlaceholder() {
    var labelText by remember { mutableStateOf("") }
    val chipPlaceholderState = rememberPlaceholderState {
        labelText.isNotEmpty()
    }

    Text(
        text = labelText,
        overflow = TextOverflow.Ellipsis,
        textAlign = TextAlign.Center,
        modifier = Modifier
            .width(90.dp)
            .placeholderShimmer(chipPlaceholderState)
            .placeholder(chipPlaceholderState)
    )

    // Simulate content loading
    LaunchedEffect(Unit) {
        delay(3000)
        labelText = "A label"
    }
    if (! chipPlaceholderState.isShowContent) {
        LaunchedEffect(chipPlaceholderState) {
            chipPlaceholderState.startPlaceholderAnimation()
        }
    }
}