PlaceholderDefaults

object PlaceholderDefaults


Contains the default values used for providing placeholders.

There are three distinct but coordinated aspects to placeholders in Compose for Wear OS. Firstly placeholder Modifier.placeholder which is drawn over content that is not yet loaded. Secondly a placeholder background which provides a background brush to cover the usual background of containers such as Button or Card until all of the content has loaded. Thirdly a placeholder shimmer effect Modifier.placeholderShimmer effect which runs in an animation loop while waiting for the data to load.

Summary

Public functions

Painter
@Composable
painterWithPlaceholderOverlayBackgroundBrush(
    placeholderState: PlaceholderState,
    originalPainter: Painter,
    color: Color
)

Create a Painter that wraps another painter and overlays a placeholder background brush on top.

Painter
@Composable
placeholderBackgroundBrush(
    placeholderState: PlaceholderState,
    color: Color
)

Create a Painter that paints with a placeholder background brush.

ButtonColors

Create a ButtonColors that can be used for a Button that is used as a placeholder drawn on top of another Button.

ButtonColors
@Composable
placeholderButtonColors(
    originalButtonColors: ButtonColors,
    placeholderState: PlaceholderState,
    color: Color
)

Create a ButtonColors that can be used in placeholder mode.

Public properties

Shape

Default Shape for Placeholder.

Public functions

painterWithPlaceholderOverlayBackgroundBrush

@Composable
fun painterWithPlaceholderOverlayBackgroundBrush(
    placeholderState: PlaceholderState,
    originalPainter: Painter,
    color: Color = MaterialTheme.colorScheme.surfaceContainer
): Painter

Create a Painter that wraps another painter and overlays a placeholder background brush on top. If the placeholderState is PlaceholderState.isHidden the original painter will be used. Otherwise the originalPainter will be drawn and then a placeholder background will be drawn over it or a wipe-off brush will be used to reveal the background when the state is PlaceholderState.isWipingOff.

Parameters
placeholderState: PlaceholderState

the state of the placeholder

originalPainter: Painter

the original painter that will be drawn over when in placeholder mode.

color: Color = MaterialTheme.colorScheme.surfaceContainer

the color to use for the placeholder background brush

placeholderBackgroundBrush

@Composable
fun placeholderBackgroundBrush(
    placeholderState: PlaceholderState,
    color: Color = MaterialTheme.colorScheme.surfaceContainer
): Painter

Create a Painter that paints with a placeholder background brush. If the placeholderState is PlaceholderState.isHidden then a transparent background will be shown. Otherwise a placeholder background will be drawn or a wipe-off brush will be used to reveal the content underneath when PlaceholderState.isWipingOff is true.

Parameters
placeholderState: PlaceholderState

the state of the placeholder

color: Color = MaterialTheme.colorScheme.surfaceContainer

the color to use for the placeholder background brush

placeholderButtonColors

@Composable
fun placeholderButtonColors(
    placeholderState: PlaceholderState,
    color: Color = MaterialTheme.colorScheme.surfaceContainer
): ButtonColors

Create a ButtonColors that can be used for a Button that is used as a placeholder drawn on top of another Button. When not drawing a placeholder background brush the button will be transparent allowing the contents of the button below to be displayed.

Example of a Button with icon and a primary and secondary labels that draws another Button over the top of it when waiting for placeholder data to load and draws over the Buttons normal background color with color as the placeholder background color which will be wiped away once all of the placeholder data is loaded:

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material3.Button
import androidx.wear.compose.material3.ButtonDefaults
import androidx.wear.compose.material3.Icon
import androidx.wear.compose.material3.PlaceholderDefaults
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.placeholder
import androidx.wear.compose.material3.placeholderShimmer
import androidx.wear.compose.material3.rememberPlaceholderState

var labelText by remember { mutableStateOf("") }
var secondaryLabelText by remember { mutableStateOf("") }
var imageVector: ImageVector? by remember { mutableStateOf(null) }

val buttonPlaceholderState = rememberPlaceholderState {
    labelText.isNotEmpty() && secondaryLabelText.isNotEmpty() && imageVector != null
}
Box {
    if (buttonPlaceholderState.isHidden || buttonPlaceholderState.isWipingOff) {
        Button(
            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 (imageVector != null) {
                    Icon(
                        imageVector = imageVector!!,
                        contentDescription = "Heart",
                        modifier =
                            Modifier.wrapContentSize(align = Alignment.Center)
                                .size(ButtonDefaults.IconSize)
                                .fillMaxSize(),
                    )
                }
            },
            colors = ButtonDefaults.filledTonalButtonColors(),
            modifier = Modifier.fillMaxWidth()
        )
    }
    if (!buttonPlaceholderState.isHidden) {
        Button(
            onClick = { /* Do something */ },
            enabled = true,
            label = {
                Box(
                    modifier =
                        Modifier.fillMaxWidth()
                            .height(16.dp)
                            .padding(top = 1.dp, bottom = 1.dp)
                            .placeholder(placeholderState = buttonPlaceholderState)
                )
            },
            secondaryLabel = {
                Box(
                    modifier =
                        Modifier.fillMaxWidth()
                            .height(16.dp)
                            .padding(top = 1.dp, bottom = 1.dp)
                            .placeholder(placeholderState = buttonPlaceholderState)
                )
            },
            icon = {
                Box(
                    modifier =
                        Modifier.size(ButtonDefaults.IconSize)
                            .placeholder(buttonPlaceholderState)
                )
                // Simulate the icon becoming ready after a period of time
                LaunchedEffect(Unit) {
                    delay(2000)
                    imageVector = Icons.Filled.Favorite
                }
            },
            colors =
                PlaceholderDefaults.placeholderButtonColors(
                    placeholderState = buttonPlaceholderState
                ),
            modifier = Modifier.fillMaxWidth().placeholderShimmer(buttonPlaceholderState)
        )
    }
}
// Simulate data being loaded after a delay
LaunchedEffect(Unit) {
    delay(2500)
    secondaryLabelText = "A secondary label"
    delay(500)
    labelText = "A label"
}
if (!buttonPlaceholderState.isHidden) {
    LaunchedEffect(buttonPlaceholderState) { buttonPlaceholderState.animatePlaceholder() }
}
Parameters
placeholderState: PlaceholderState

the current placeholder state.

color: Color = MaterialTheme.colorScheme.surfaceContainer

the color to use for the placeholder background brush.

placeholderButtonColors

@Composable
fun placeholderButtonColors(
    originalButtonColors: ButtonColors,
    placeholderState: PlaceholderState,
    color: Color = MaterialTheme.colorScheme.surfaceContainer
): ButtonColors

Create a ButtonColors that can be used in placeholder mode. This will provide the placeholder background effect that covers the normal button background with a solid background of color when the placeholderState is set to show the placeholder and a wipe off gradient brush when the state is in wipe-off mode. If the state is PlaceholderState.isHidden then the normal background will be used. All other colors will be delegated to originalButtonColors.

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 and draws over the Buttons normal background color with color as the placeholder background color which will be wiped away once all of the placeholder data is loaded:

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.style.TextOverflow
import androidx.wear.compose.material3.Button
import androidx.wear.compose.material3.ButtonDefaults
import androidx.wear.compose.material3.Icon
import androidx.wear.compose.material3.PlaceholderDefaults
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.placeholder
import androidx.wear.compose.material3.placeholderShimmer
import androidx.wear.compose.material3.rememberPlaceholderState

var labelText by remember { mutableStateOf("") }
var imageVector: ImageVector? by remember { mutableStateOf(null) }
val buttonPlaceholderState = rememberPlaceholderState {
    labelText.isNotEmpty() && imageVector != null
}

Button(
    onClick = { /* Do something */ },
    enabled = true,
    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(),
                )
            }
        }
    },
    colors =
        PlaceholderDefaults.placeholderButtonColors(
            originalButtonColors = ButtonDefaults.buttonColors(),
            placeholderState = buttonPlaceholderState
        ),
    modifier = Modifier.fillMaxWidth().placeholderShimmer(buttonPlaceholderState)
)
// Simulate content loading completing in stages
LaunchedEffect(Unit) {
    delay(2000)
    imageVector = Icons.Filled.Favorite
    delay(1000)
    labelText = "A label"
}
if (!buttonPlaceholderState.isHidden) {
    LaunchedEffect(buttonPlaceholderState) { buttonPlaceholderState.animatePlaceholder() }
}
Parameters
originalButtonColors: ButtonColors

the button colors to use when not in placeholder mode.

placeholderState: PlaceholderState

the placeholder state of the component

color: Color = MaterialTheme.colorScheme.surfaceContainer

the color to use for the placeholder background brush

Public properties

Shape

Added in 1.0.0-alpha28
val ShapeShape

Default Shape for Placeholder.