Skip to content

Instantly share code, notes, and snippets.

@Andrew0000
Last active May 16, 2025 18:37
Show Gist options
  • Save Andrew0000/3edb9c25ebc20a2935c9ff4805e05f5d to your computer and use it in GitHub Desktop.
Save Andrew0000/3edb9c25ebc20a2935c9ff4805e05f5d to your computer and use it in GitHub Desktop.
Jetpack Compose custom shadow with dx, dy and radius
import android.graphics.BlurMaskFilter
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@Preview(
widthDp = 160,
heightDp = 160,
showBackground = true,
backgroundColor = 0xffffff,
)
@Composable
private fun Preview() {
Box(
modifier = Modifier
.requiredSize(100.dp)
.background(Color.Yellow),
) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(25.dp)
.shadowCustom(
offsetX = 8.dp,
offsetY = 8.dp,
blurRadius = 8.dp,
),
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Green),
) {
}
}
}
}
fun Modifier.shadowCustom(
color: Color = Color.Black,
offsetX: Dp = 0.dp,
offsetY: Dp = 0.dp,
blurRadius: Dp = 0.dp,
shapeRadius: Dp = 0.dp,
) = composed {
val paint: Paint = remember { Paint() }
val blurRadiusPx = blurRadius.px(LocalDensity.current)
val maskFilter = remember {
BlurMaskFilter(blurRadiusPx, BlurMaskFilter.Blur.NORMAL)
}
drawBehind {
drawIntoCanvas { canvas ->
val frameworkPaint = paint.asFrameworkPaint()
if (blurRadius != 0.dp) {
frameworkPaint.maskFilter = maskFilter
}
frameworkPaint.color = color.toArgb()
val leftPixel = offsetX.toPx()
val topPixel = offsetY.toPx()
val rightPixel = size.width + leftPixel
val bottomPixel = size.height + topPixel
if (shapeRadius > 0.dp) {
val radiusPx = shapeRadius.toPx()
canvas.drawRoundRect(
left = leftPixel,
top = topPixel,
right = rightPixel,
bottom = bottomPixel,
radiusX = radiusPx,
radiusY = radiusPx,
paint = paint,
)
} else {
canvas.drawRect(
left = leftPixel,
top = topPixel,
right = rightPixel,
bottom = bottomPixel,
paint = paint,
)
}
}
}
}
private fun Dp.px(density: Density): Float =
with(density) { toPx() }
@Andrew0000
Copy link
Author

Andrew0000 commented Aug 7, 2023

@qixiaoo Yes, thank you. I updated the gist

@bhavin-android
Copy link

@Andrew0000 thanks It's help me to give shadow to NavigationBar

@likeich
Copy link

likeich commented Nov 16, 2024

Thank you for making this! I modified it to work with Jetpack Compose shapes for my project.

fun Modifier.shadowCustom(
    color: Color = Color.Black,
    offsetX: Dp = 0.dp,
    offsetY: Dp = 0.dp,
    blurRadius: Dp = 0.dp,
    shape: Shape = RectangleShape,
) = composed {
    require(blurRadius > 0.dp) { "Blur radius must be greater than 0" }

    val paint: Paint = remember { Paint() }
    val blurRadiusPx = blurRadius.toPx(LocalDensity.current)
    val offsetXpx = offsetX.toPx(LocalDensity.current)
    val offsetYpx = offsetY.toPx(LocalDensity.current)

    val maskFilter = remember {
        BlurMaskFilter(blurRadiusPx, BlurMaskFilter.Blur.NORMAL)
    }

    drawBehind {
        drawIntoCanvas { canvas ->
            val frameworkPaint = paint.asFrameworkPaint()
            if (blurRadius != 0.dp) {
                frameworkPaint.maskFilter = maskFilter
            }
            frameworkPaint.color = color.toArgb()

            val outline = shape.createOutline(size, LayoutDirection.Ltr, this)

            // Apply the offset by translating the canvas
            canvas.save()
            canvas.translate(offsetXpx, offsetYpx)
            canvas.drawOutline(
                outline = outline,
                paint = paint
            )
            canvas.restore()
        }
    }
}

private fun Dp.toPx(density: Density): Float = with(density) { toPx() }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment