Skip to content

Native shadows

zed-alpha edited this page May 10, 2026 · 1 revision

Package: com.zedalpha.shadowgadgets.compose


The compose package contains two Modifier extensions (with overloads) to to replace the inbuilt shadow.


Modifier.clippedShadow

clippedShadow behaves just like shadow, and the base function has the exact same signature:

fun Modifier.clippedShadow(
    elevation: Dp,
    shape: Shape = RectangleShape,
    clip: Boolean = elevation > 0.dp,
    ambientColor: Color = DefaultShadowColor,
    spotColor: Color = DefaultShadowColor
)

Please note that the clip parameter has nothing to do with the clipped shadow itself. That parameter means the same thing as it does for shadow: "When active, the content drawing clips to the shape."

If this is being used to replace the shadow on an existing Composable, it's important to ensure that the intrinsic shadow is disabled, if it has one, by zeroing its elevation. For example:

Card(
    backgroundColor = Color.Transparent,
    elevation = 0.dp,
    shape = RoundedCornerShape(15.dp),
    modifier = Modifier
        .size(100.dp)
        .clippedShadow(
            elevation = 10.dp,
            shape = RoundedCornerShape(15.dp)
        )
) {}

Color compat

Color compat has been added to clippedShadow with an overload:

fun Modifier.clippedShadow(
    elevation: Dp,
    shape: Shape = RectangleShape,
    clip: Boolean = elevation > 0.dp,
    ambientColor: Color = DefaultShadowColor,
    spotColor: Color = DefaultShadowColor,
    colorCompat: Color = DefaultShadowColor,
    forceColorCompat: Boolean = false
)

forceColorCompat acts like the corresponding View property to force the compat mechanism to be used on API levels >= 28, for testing and comparisons and whatnot.

The colorCompat parameter defaults to black, which disables the tinting, as it's the default shadow color. If Color.Unspecified is passed, the actual value will be blended as described below for shadowCompat.

Lambda

There is also an overload that takes a lambda to allow for more efficient updates for animations and such.

fun Modifier.clippedShadow(
    shape: Shape,
    clip: Boolean = true,
    block: ClippedShadowScope.() -> Unit
): Modifier

Changing the shape or the clip will clearly need to recompose, but the rest of the parameters – elevation, ambientColor, spotColor, colorCompat, and forceColorCompat – now live inside ClippedShadowScope, and each can be modified in block without causing recomposition.


Modifier.shadowCompat

The shadowCompat modifier is useful when only the color compat functionality is needed, without the clip, in order to save some overhead:

fun Modifier.shadowCompat(
    elevation: Dp,
    shape: Shape = RectangleShape,
    clip: Boolean = elevation > 0.dp,
    ambientColor: Color = DefaultShadowColor,
    spotColor: Color = DefaultShadowColor,
    colorCompat: Color = Color.Unspecified,
    forceColorCompat: Boolean = false
)

It has the same parameter list as the clippedShadow overload, but here colorCompat's default value is Color.Unspecified, which causes it to be automatically calculated as a blend of the supplied ambientColor and spotColor, in the same manner as described for ShadowColorsBlender on the Color compat wiki page. This behavior can be disabled by passing any other value for colorCompat.

This modifier falls back to the inbuilt shadow whenever possible, e.g., if colorCompat is disabled, or if the resulting blend would be black.

Lambda

There is a lambda overload for this function as well:

fun Modifier.shadowCompat(
    shape: Shape,
    clip: Boolean = true,
    block: ShadowCompatScope.() -> Unit
): Modifier

Like clippedShadow, changing the shape or the clip will recompose, but the other parameters – elevation, ambientColor, spotColor, colorCompat, and forceColorCompat – are now in ShadowCompatScope and can be updated without recomposition.


Examples

The Compose page of the demo app has examples of clippedShadow's basic usage, as well as demonstrations of how to replace the shadows on existing material Composables with animated elevations – e.g., FloatingActionButton – without having to rewrite them or fiddle with their internals.

The first color compat page is set up to demonstrate shadowCompat, with automatic color blending too, alongside the analogous View version.

The third color compat page has a stress test setup for Compose right next to one for Views that is identical, apart from some minor irrelevant color variations, so they can be compared side by side.

Examples of both lambda versions can be found on their topic page:

Clone this wiki locally