I’m working on a project using Jetpack Compose in Kotlin and I’m trying to apply a blur effect to the entire screen, excluding a specific rectangle. The goal is to blur the background, regardless of its content (it could be a photo or other UI elements), and have a clear, non-blurred rectangle within this blurred background.
Here’s an example of what I’m trying to achieve:
I found a similar example where an overlay of another transparent color is used instead of blurring. Here’s the code:
@Preview(showBackground = true)
@Composable
fun BlurExample()
{
Image(
painter = painterResource(id = R.drawable.delete1),
contentDescription = null,
contentScale = ContentScale.FillBounds,
modifier = Modifier.fillMaxSize().blur(20.dp)
)
TransparentClipLayout(
modifier = Modifier.fillMaxSize(),
width = 300.dp,
height = 200.dp,
offsetY = 150.dp
)
}
@Composable
fun TransparentClipLayout(
modifier: Modifier,
width: Dp,
height: Dp,
offsetY: Dp
) {
val offsetInPx: Float
val widthInPx: Float
val heightInPx: Float
with(LocalDensity.current) {
offsetInPx = offsetY.toPx()
widthInPx = width.toPx()
heightInPx = height.toPx()
}
Canvas(modifier = modifier) {
with(drawContext.canvas.nativeCanvas) {
val checkPoint = saveLayer(null, null)
// Destination
drawRect(Color(0x77FF0000))
// Source
drawRoundRect(
topLeft = Offset(
x = (size.width - widthInPx) / 2,
y = offsetInPx
),
size = Size(widthInPx, heightInPx),
cornerRadius = CornerRadius(30f,30f),
color = Color.Transparent,
blendMode = BlendMode.Clear
)
restoreToCount(checkPoint)
}
}
}
However, I haven’t been able to find a solution on how to apply a blur effect to the background. My question is: How can I achieve a clear, non-blurred rectangle within a blurred background, similar to the example above, using Jetpack Compose in Kotlin?
Note: I understand that Modifier.blur can be used to apply a blur effect, but it seems to blur the entire background, including the rectangle that I want to keep clear.
Somehow blur doesn't draw when there is no content. If it's ok to draw image twice you can do it like this
@Preview(showBackground = true)
@Composable
fun BlurExample() {
val offsetInPx: Float
val widthInPx: Float
val heightInPx: Float
with(LocalDensity.current) {
offsetInPx = 150.dp.toPx()
widthInPx = 300.dp.toPx()
heightInPx = 200.dp.toPx()
}
Box {
Image(
painter = painterResource(id = R.drawable.landscape11),
contentDescription = null,
contentScale = ContentScale.FillBounds,
modifier = Modifier.fillMaxSize()
)
Box(modifier = Modifier
.matchParentSize()
.blur(20.dp, edgeTreatment = BlurredEdgeTreatment.Unbounded)
.drawWithContent {
with(drawContext.canvas.nativeCanvas) {
val checkPoint = saveLayer(null, null)
// Destination
drawContent()
// Source
drawRoundRect(
topLeft = Offset(
x = (size.width - widthInPx) / 2,
y = offsetInPx
),
size = Size(widthInPx, heightInPx),
cornerRadius = CornerRadius(30f, 30f),
color = Color.Transparent,
blendMode = BlendMode.Clear
)
restoreToCount(checkPoint)
}
}
) {
Image(
painter = painterResource(id = R.drawable.landscape11),
contentDescription = null,
contentScale = ContentScale.FillBounds,
modifier = Modifier.fillMaxSize()
)
}
}
}
Or like this
@Preview(showBackground = true)
@Composable
fun BlurExample2() {
val offsetInPx: Float
val widthInPx: Float
val heightInPx: Float
with(LocalDensity.current) {
offsetInPx = 150.dp.toPx()
widthInPx = 300.dp.toPx()
heightInPx = 200.dp.toPx()
}
val painter = painterResource(R.drawable.landscape11)
Box(modifier = Modifier.fillMaxSize()
.drawBehind {
with(painter){
draw(size)
}
}
.drawWithContent {
with(drawContext.canvas.nativeCanvas) {
val checkPoint = saveLayer(null, null)
// Destination
drawContent()
// Source
drawRoundRect(
topLeft = Offset(
x = (size.width - widthInPx) / 2,
y = offsetInPx
),
size = Size(widthInPx, heightInPx),
cornerRadius = CornerRadius(30f, 30f),
color = Color.Transparent,
blendMode = BlendMode.Clear
)
restoreToCount(checkPoint)
}
}
.blur(20.dp)
.drawBehind {
with(painter){
draw(size)
}
}
)
}