How to create an inner shadow with Jetpack Compose? The gradient should run from black on the outside to transparent on the inside. Modifier.shadow()
is just for outer shadows. Using negative elevation is not working.
fun Modifier.innerShadow(
color: Color = Color.Black,
cornersRadius: Dp = 0.dp,
spread: Dp = 0.dp,
blur: Dp = 0.dp,
offsetY: Dp = 0.dp,
offsetX: Dp = 0.dp
) = drawWithContent {
drawContent()
val rect = Rect(Offset.Zero, size)
val paint = Paint()
drawIntoCanvas {
paint.color = color
paint.isAntiAlias = true
it.saveLayer(rect, paint)
it.drawRoundRect(
left = rect.left,
top = rect.top,
right = rect.right,
bottom = rect.bottom,
cornersRadius.toPx(),
cornersRadius.toPx(),
paint
)
val frameworkPaint = paint.asFrameworkPaint()
frameworkPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT)
if (blur.toPx() > 0) {
frameworkPaint.maskFilter = BlurMaskFilter(blur.toPx(), BlurMaskFilter.Blur.NORMAL)
}
val left = if (offsetX > 0.dp) {
rect.left + offsetX.toPx()
} else {
rect.left
}
val top = if (offsetY > 0.dp) {
rect.top + offsetY.toPx()
} else {
rect.top
}
val right = if (offsetX < 0.dp) {
rect.right + offsetX.toPx()
} else {
rect.right
}
val bottom = if (offsetY < 0.dp) {
rect.bottom + offsetY.toPx()
} else {
rect.bottom
}
paint.color = Color.Black
it.drawRoundRect(
left = left + spread.toPx() / 2,
top = top + spread.toPx() / 2,
right = right - spread.toPx() / 2,
bottom = bottom - spread.toPx() / 2,
cornersRadius.toPx(),
cornersRadius.toPx(),
paint
)
frameworkPaint.xfermode = null
frameworkPaint.maskFilter = null
}
}
Usage:
Box(
modifier = Modifier
.width(240.dp)
.height(180.dp)
.outerShadow(
color = Color(0xff000000),
alpha = 0.5f,
cornersRadius = 20.dp,
shadowBlurRadius = 30.dp,
offsetX = 0.dp,
offsetY = 15.dp
)
.clip(RoundedCornerShape(20.dp))
.background(Color(0xFF282A2F))
.innerShadow(
blur = 1.dp,
color = Color(0xff00FFFF),
cornersRadius = 20.dp,
offsetX = (-40.5).dp,
offsetY = (-10.5).dp
)
.innerShadow(
blur = 20.dp,
color = Color(0xffff0000),
cornersRadius = 20.dp,
offsetX = 0.5.dp,
offsetY = 0.5.dp
)
.padding(14.dp),
contentAlignment = Alignment.Center
) {
Text(
text = stringResource(id = R.string.notification_service),
modifier = Modifier,
color = Color.White
)
}
I hope it helps. outerShadow
is custom modifier too, copied from other websites.
Position of Modifier.shadow
relative to Modifier.background places shadow inside your component. Also you can create Modifier.drawWithContent and frameworkPaint a show with blur.
Column(
modifier = Modifier
.fillMaxSize()
.padding(20.dp)
) {
ComponentWithInnerShadow()
Spacer(modifier = Modifier.height(12.dp))
ComponentWithOuterShadow()
Spacer(modifier = Modifier.height(12.dp))
ComponentWithCustomInnerShadow()
}
@Composable private fun ComponentWithInnerShadow() {
Column(
modifier = Modifier
.clip(RoundedCornerShape(5.dp))
.background(Color.Yellow)
.shadow(2.dp, shape = RoundedCornerShape(5.dp))
) {
Text(text = "Hello World", modifier = Modifier.padding(12.dp))
} }
@Composable
private fun ComponentWithOuterShadow() {
Column(
modifier = Modifier
.shadow(2.dp, shape = RoundedCornerShape(5.dp))
.background(Color.Yellow)
) {
Text(text = "Hello World", modifier = Modifier.padding(12.dp))
}
}
@Composable
private fun ComponentWithCustomInnerShadow() {
Column(
modifier = Modifier.innerShadow()
) {
Text(text = "Hello World", modifier = Modifier.padding(12.dp))
}
}
And with composed modifier. I didn't set parameters just set arbitrary numbers you can set your own parameters, and set color if you want to. This one looks better than standard inner shadow though. You need to provide color for foreground and shape with this one either. I set color and drew Rounded rectangle for demonstration
fun Modifier.innerShadow() = composed(
inspectorInfo = {
},
factory = {
val paint = remember() {
Paint()
}
val foregroundPaint = remember() {
Paint().apply {
color = Color.Yellow
}
}
val frameworkPaint = remember {
paint.asFrameworkPaint()
}
Modifier.drawWithContent {
this.drawIntoCanvas {
val color = Color.LightGray
val radius = 2.dp.toPx()
val shadowColor = color
.copy(alpha = .7f)
.toArgb()
val transparent = color
.copy(alpha = 0f)
.toArgb()
frameworkPaint.color = transparent
frameworkPaint.setShadowLayer(
radius,
0f,
0f,
shadowColor
)
val shadowRadius = 4.dp.toPx()
it.drawRoundRect(
left = 0f,
top = 0f,
right = this.size.width,
bottom = this.size.height,
radiusX = 5.dp.toPx(),
radiusY = 5.dp.toPx(),
paint = foregroundPaint
)
it.drawRoundRect(
left = 0f,
top = 0f,
right = this.size.width,
bottom = this.size.height,
radiusX = 5.dp.toPx(),
radiusY = 5.dp.toPx(),
paint = paint
)
it.drawRoundRect(
left = shadowRadius,
top = shadowRadius,
right = this.size.width - shadowRadius,
bottom = this.size.height - shadowRadius,
radiusX = 5.dp.toPx(),
radiusY = 5.dp.toPx(),
paint = foregroundPaint
)
drawContent()
}
}
}
)
This is not supported but you have alternative options:
Modifier.drawBehind
to draw the inset shadow underneath the content
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.