
GPU-accelerated blur modifier for Compose UI with NEON-optimized CPU fallback, bitmap capture, adjustable radius, and observable blurring states for applied, captured, loading, and error.
Kotlin Multiplatform blur and liquid glass effect library for Compose, with GPU-accelerated rendering and CPU fallback for older devices. See documentation for more details.
The `blur` modifier supports only Android 12 and higher, and `RenderScript` APIs are deprecated starting in Android 12. Cloudy is the backport of the blur effect for Jetpack Compose with cross-platform support.
| Platform | Implementation | Performance | State Type |
|---|---|---|---|
| Android 31+ | RenderEffect (GPU) | GPU-accelerated | Success.Applied |
| Android 30- | Native C++ (CPU) | NEON/SIMD optimized | Success.Captured |
| iOS | Skia BlurEffect (Metal GPU) | GPU-accelerated | Success.Applied |
| macOS | Skia BlurEffect (Metal GPU) | GPU-accelerated | Success.Applied |
| Desktop (JVM) | Skia BlurEffect (GPU) | GPU-accelerated | Success.Applied |
| WASM (Browser) | Skia BlurEffect (WebGL) | GPU-accelerated | Success.Applied |
See Liquid Glass Effect for liquid glass platform support.
If you're using Version Catalog, you can configure the dependency by adding it to your libs.versions.toml file as follows:
[versions]
#...
cloudy = "0.6.1"
[libraries]
#...
compose-cloudy = { module = "com.github.skydoves:cloudy", version.ref = "cloudy" }Add the dependency below to your module's build.gradle.kts file:
dependencies {
implementation("com.github.skydoves:cloudy:0.6.1")
// if you're using Version Catalog
implementation(libs.compose.cloudy)
}For Kotlin Multiplatform, add the dependency below to your module's build.gradle.kts file:
sourceSets {
commonMain.dependencies {
implementation("com.github.skydoves:cloudy:$version")
}
}Cloudy offers two blur modes: Modifier.cloudy(radius = …) blurs a composable's own content (covered below), while Background Blur blurs the content behind a composable for glassmorphism surfaces.
You can implement blur effect with Modifier.cloudy() composable function as seen below:
Text(
modifier = Modifier.cloudy(),
text = "This text is blurred"
)You can change the degree of the blur effect by changing the radius parameter of Modifier.cloudy() composable function.
Column(
modifier = Modifier.cloudy(radius = 15)
) {
Image(..)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
text = posterModel.name,
fontSize = 40.sp,
color = MaterialTheme.colors.onBackground,
textAlign = TextAlign.Center
)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
text = posterModel.description,
color = MaterialTheme.colors.onBackground,
textAlign = TextAlign.Center
)
}You can monitor the status of the blurring effect by using the onStateChanged parameter, which provides CloudyState. This allows you to observe and respond to changes in the blurring effect's state effectively.
GlideImage(
modifier = Modifier
.size(400.dp)
.cloudy(
radius = 25,
onStateChanged = { state ->
when (state) {
is CloudyState.Success.Applied -> {
// GPU blur applied (iOS, Android 31+)
// No bitmap available - blur rendered directly
}
is CloudyState.Success.Captured -> {
// CPU blur completed (Android 30-)
// Blurred bitmap available: state.bitmap
val blurredBitmap = state.bitmap
}
is CloudyState.Loading -> {
// Blur processing in progress
}
is CloudyState.Error -> {
// Handle error: state.throwable
}
CloudyState.Nothing -> {
// Initial state
}
}
}
),
..
)| State | Description | Bitmap Available |
|---|---|---|
Success.Applied |
GPU blur applied in rendering pipeline | No |
Success.Captured |
CPU blur completed with bitmap | Yes (state.bitmap) |
Success.Scrim |
Scrim overlay shown instead of blur (background blur on Android 30-) | No |
Loading |
Blur processing in progress | No |
Error |
Blur operation failed | No |
Nothing |
Initial state | No |
The Modifier.cloudy captures the bitmap of the composable node under the hood.
LazyVerticalGrid(
state = rememberLazyGridState(),
columns = GridCells.Fixed(2)
) {
itemsIndexed(key = { index, item -> item.id }, items = posters) { index, item ->
HomePoster(poster = item)
}
}
@Composable
private fun HomePoster(poster: Poster) {
ConstraintLayout {
val (image, title, content) = createRefs()
GlideImage(
modifier = Modifier
.cloudy(radius = 15)
.aspectRatio(0.8f)
.constrainAs(image) {
centerHorizontallyTo(parent)
top.linkTo(parent.top)
}
..Background blur — also called backdrop blur — blurs the content that sits behind a composable, producing a glassmorphism / frosted-glass surface such as a translucent app bar, bottom navigation, or card. Unlike Modifier.cloudy(radius = …), which blurs a composable's own content, the backdrop API samples a shared snapshot of the background and renders it blurred underneath your overlay.
It is built from three pieces:
| API | Role |
|---|---|
rememberSky() |
Creates a Sky — the shared state holder for the captured background. |
Modifier.sky(sky) |
Marks the source container whose content is captured for blur. |
Modifier.cloudy(sky = sky, …) |
The overlay that draws the captured background, blurred and clipped to its bounds. |
https://github.com/user-attachments/assets/c22cb656-4415-471e-a30a-521d39344447
https://github.com/user-attachments/assets/faf67c77-cb1e-4b20-994b-bb8afe340087
Put Modifier.sky(sky) on the background you want to blur, then overlay a composable with Modifier.cloudy(sky = sky, …):
val sky = rememberSky()
Box(modifier = Modifier.fillMaxSize()) {
// 1) Source — the content captured for blur
LazyVerticalGrid(
columns = GridCells.Fixed(2),
modifier = Modifier.sky(sky),
) {
items(posters) { poster -> GridPosterItem(poster) }
}
// 2) Overlay — a frosted glass app bar that blurs the grid behind it
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.cloudy(
sky = sky,
radius = 20,
tint = MaterialTheme.colorScheme.surface.copy(alpha = 0.3f),
)
) {
Text("Frosted Glass App Bar")
}
}By default the blurred backdrop is clipped to a rectangle. Pass a shape so the blur follows rounded corners instead of leaving a hard rectangular box inside a rounded glass surface:
val barShape = RoundedCornerShape(36.dp)
Row(
modifier = Modifier
.clip(barShape)
.cloudy(sky = sky, radius = 24, shape = barShape)
.background(Color.White.copy(alpha = 0.15f)),
) {
// tabs
}Use the progressive parameter to fade the blur across the surface — ideal for scroll-edge fades or vignettes:
// Blur is strongest at the top and fades to clear toward the bottom
Modifier.cloudy(
sky = sky,
radius = 25,
progressive = CloudyProgressive.TopToBottom(),
)| Mode | Effect |
|---|---|
CloudyProgressive.None |
Uniform blur (default) |
CloudyProgressive.TopToBottom(start, end, easing) |
Blur fades from top → bottom |
CloudyProgressive.BottomToTop(start, end, easing) |
Blur fades from bottom → top |
CloudyProgressive.Edges(fadeDistance, easing) |
Blur at the edges, clear in the center (vignette) |
Progressive blur uses an AGSL shader on Android 33+ and Skia shaders on iOS/macOS/Desktop/WASM. On Android 32 and below it falls back to uniform blur.
The backdrop refreshes automatically while the background scrolls or animates, and parks at zero frames once it settles. When the background changes outside of a scroll/animation (for example, an image finishes loading or you swap content), call Sky.invalidate() to re-capture:
AsyncImage(
model = imageUrl,
modifier = Modifier.sky(sky),
onSuccess = { sky.invalidate() },
)For an animated change that lasts a known duration (e.g. a tab cross-fade), pass the duration so the blur tracks the whole transition instead of freezing once the short settle tail elapses:
LaunchedEffect(selectedTab) {
sky.invalidate(durationMillis = 240) // matches the cross-fade duration
}| Parameter | Default | Description |
|---|---|---|
sky |
– | The Sky holding the captured background (required). |
radius |
20 (CloudyDefaults.BACKGROUND_RADIUS) |
Blur radius in pixels. Must be non-negative. |
progressive |
CloudyProgressive.None |
Gradient blur configuration. |
tint |
Color.Transparent |
Optional color blended over the blurred backdrop. |
enabled |
true |
When false, the effect is disabled (renders nothing). |
cpuBlurEnabled |
false (CloudyDefaults.CPP_BLUR_ENABLED) |
Enable CPU blur on Android 30-; otherwise a scrim is shown. |
shape |
RectangleShape |
Shape the blurred backdrop is clipped to. |
onStateChanged |
{} |
Callback for CloudyState changes. |
| Platform | API Level | Implementation | Progressive Blur |
|---|---|---|---|
| Android | 33+ | AGSL RuntimeShader | Supported |
| Android | 31–32 | RenderEffect | Uniform only |
| Android | 30- | Bitmap + CPU (if cpuBlurEnabled) or scrim |
Uniform only |
| iOS / macOS / Desktop / WASM | – | Skia BlurEffect | Supported |
On Android 30 and below, CPU blur is disabled by default and a semi-transparent scrim (
CloudyState.Success.Scrim) is shown instead, following a performance-first approach. SetcpuBlurEnabled = trueto force CPU blur on those devices.
You can easily implement blur effect with Landscapist, which is a Jetpack Compose image loading library that fetches and displays network images with Glide, Coil, and Fresco. For more information, see the Transformation section.
Cloudy provides a Modifier.liquidGlass() that creates a realistic glass lens effect with SDF-based crisp edges, normal-based refraction, and chromatic dispersion.
Note: For blur effects, use Modifier.cloudy() separately. The two modifiers are designed to work independently, giving you full control over each effect.
| Platform | Implementation | Features |
|---|---|---|
| Android 33+ | RuntimeShader (AGSL) | Full effect |
| Android 32- | Fallback | Tint + edge + shape (no lens refraction) |
| iOS | Skia RuntimeEffect | Full effect |
| macOS | Skia RuntimeEffect | Full effect |
| Desktop (JVM) | Skia RuntimeEffect | Full effect |
| WASM | Skia RuntimeEffect | Full effect |
var lensCenter by remember { mutableStateOf(Offset(100f, 100f)) }
Box(
modifier = Modifier
.fillMaxSize()
.pointerInput(Unit) {
detectDragGestures { change, _ ->
lensCenter = change.position
}
}
.cloudy(radius = 15) // Use Cloudy for blur (independent)
.liquidGlass(lensCenter = lensCenter) // Lens distortion effect
) {
Image(
painter = painterResource(R.drawable.photo),
contentDescription = null,
modifier = Modifier.fillMaxSize()
)
}The liquidGlass() and cloudy() modifiers are independent and composable:
// Blur only
.cloudy(radius = 20)
// Lens effect only
.liquidGlass(lensCenter = lensCenter)
// Both effects combined
.cloudy(radius = 15)
.liquidGlass(lensCenter = lensCenter)This separation gives you full access to Cloudy's blur API (radius, onStateChanged, CloudyState) while using the liquid glass lens effect.
You can customize the liquid glass effect with various parameters:
.liquidGlass(
lensCenter = lensCenter,
lensSize = Size(350f, 350f), // Size of the glass lens
cornerRadius = 50f, // Rounded corners
refraction = 0.25f, // Distortion amount
curve = 0.25f, // Lens curvature strength
dispersion = 0.0f, // Chromatic dispersion (RGB separation)
saturation = 1.0f, // Color saturation
contrast = 1.0f, // Light/dark contrast
tint = Color.Transparent, // Optional color tint
edge = 0.2f, // Edge lighting width
)| Parameter | Default | Description | Fallback (Android 32-) |
|---|---|---|---|
lensCenter |
- | Center position of the glass lens (required) | Yes |
lensSize |
350x350 | Size of the lens in pixels | Yes |
cornerRadius |
50f | Corner radius for rounded rectangle shape | Yes |
refraction |
0.25f | Controls how much background distorts through lens | No (requires API 33+) |
curve |
0.25f | Controls how strongly lens curves at center vs edges | No (requires API 33+) |
dispersion |
0.0f | Chromatic dispersion/aberration intensity | No (requires API 33+) |
saturation |
1.0f | Color saturation (1.0 = normal) | Approximation |
contrast |
1.0f | Light/dark contrast (1.0 = normal) | Approximation |
tint |
Transparent | Optional color tint overlay | Yes |
edge |
0.2f | Edge lighting width (0 = none) | Yes (as stroke) |
enabled |
true | Enable/disable the effect | Yes |
Note: On Android 32 and below, the lens refraction effect is not available since it requires
RuntimeShader(API 33+). The fallback draws a visible lens shape with tint, edge lighting, and color adjustments. For blur effects, useModifier.cloudy()separately.
Support it by joining stargazers for this repository. ⭐
Also, follow me on GitHub for my next creations! 🤩
The liquid glass shader implementation was inspired by FletchMcKee/liquid.
Designed and developed by 2022 skydoves (Jaewoong Eum)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.Kotlin Multiplatform blur and liquid glass effect library for Compose, with GPU-accelerated rendering and CPU fallback for older devices. See documentation for more details.
The `blur` modifier supports only Android 12 and higher, and `RenderScript` APIs are deprecated starting in Android 12. Cloudy is the backport of the blur effect for Jetpack Compose with cross-platform support.
| Platform | Implementation | Performance | State Type |
|---|---|---|---|
| Android 31+ | RenderEffect (GPU) | GPU-accelerated | Success.Applied |
| Android 30- | Native C++ (CPU) | NEON/SIMD optimized | Success.Captured |
| iOS | Skia BlurEffect (Metal GPU) | GPU-accelerated | Success.Applied |
| macOS | Skia BlurEffect (Metal GPU) | GPU-accelerated | Success.Applied |
| Desktop (JVM) | Skia BlurEffect (GPU) | GPU-accelerated | Success.Applied |
| WASM (Browser) | Skia BlurEffect (WebGL) | GPU-accelerated | Success.Applied |
See Liquid Glass Effect for liquid glass platform support.
If you're using Version Catalog, you can configure the dependency by adding it to your libs.versions.toml file as follows:
[versions]
#...
cloudy = "0.6.1"
[libraries]
#...
compose-cloudy = { module = "com.github.skydoves:cloudy", version.ref = "cloudy" }Add the dependency below to your module's build.gradle.kts file:
dependencies {
implementation("com.github.skydoves:cloudy:0.6.1")
// if you're using Version Catalog
implementation(libs.compose.cloudy)
}For Kotlin Multiplatform, add the dependency below to your module's build.gradle.kts file:
sourceSets {
commonMain.dependencies {
implementation("com.github.skydoves:cloudy:$version")
}
}Cloudy offers two blur modes: Modifier.cloudy(radius = …) blurs a composable's own content (covered below), while Background Blur blurs the content behind a composable for glassmorphism surfaces.
You can implement blur effect with Modifier.cloudy() composable function as seen below:
Text(
modifier = Modifier.cloudy(),
text = "This text is blurred"
)You can change the degree of the blur effect by changing the radius parameter of Modifier.cloudy() composable function.
Column(
modifier = Modifier.cloudy(radius = 15)
) {
Image(..)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
text = posterModel.name,
fontSize = 40.sp,
color = MaterialTheme.colors.onBackground,
textAlign = TextAlign.Center
)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
text = posterModel.description,
color = MaterialTheme.colors.onBackground,
textAlign = TextAlign.Center
)
}You can monitor the status of the blurring effect by using the onStateChanged parameter, which provides CloudyState. This allows you to observe and respond to changes in the blurring effect's state effectively.
GlideImage(
modifier = Modifier
.size(400.dp)
.cloudy(
radius = 25,
onStateChanged = { state ->
when (state) {
is CloudyState.Success.Applied -> {
// GPU blur applied (iOS, Android 31+)
// No bitmap available - blur rendered directly
}
is CloudyState.Success.Captured -> {
// CPU blur completed (Android 30-)
// Blurred bitmap available: state.bitmap
val blurredBitmap = state.bitmap
}
is CloudyState.Loading -> {
// Blur processing in progress
}
is CloudyState.Error -> {
// Handle error: state.throwable
}
CloudyState.Nothing -> {
// Initial state
}
}
}
),
..
)| State | Description | Bitmap Available |
|---|---|---|
Success.Applied |
GPU blur applied in rendering pipeline | No |
Success.Captured |
CPU blur completed with bitmap | Yes (state.bitmap) |
Success.Scrim |
Scrim overlay shown instead of blur (background blur on Android 30-) | No |
Loading |
Blur processing in progress | No |
Error |
Blur operation failed | No |
Nothing |
Initial state | No |
The Modifier.cloudy captures the bitmap of the composable node under the hood.
LazyVerticalGrid(
state = rememberLazyGridState(),
columns = GridCells.Fixed(2)
) {
itemsIndexed(key = { index, item -> item.id }, items = posters) { index, item ->
HomePoster(poster = item)
}
}
@Composable
private fun HomePoster(poster: Poster) {
ConstraintLayout {
val (image, title, content) = createRefs()
GlideImage(
modifier = Modifier
.cloudy(radius = 15)
.aspectRatio(0.8f)
.constrainAs(image) {
centerHorizontallyTo(parent)
top.linkTo(parent.top)
}
..Background blur — also called backdrop blur — blurs the content that sits behind a composable, producing a glassmorphism / frosted-glass surface such as a translucent app bar, bottom navigation, or card. Unlike Modifier.cloudy(radius = …), which blurs a composable's own content, the backdrop API samples a shared snapshot of the background and renders it blurred underneath your overlay.
It is built from three pieces:
| API | Role |
|---|---|
rememberSky() |
Creates a Sky — the shared state holder for the captured background. |
Modifier.sky(sky) |
Marks the source container whose content is captured for blur. |
Modifier.cloudy(sky = sky, …) |
The overlay that draws the captured background, blurred and clipped to its bounds. |
https://github.com/user-attachments/assets/c22cb656-4415-471e-a30a-521d39344447
https://github.com/user-attachments/assets/faf67c77-cb1e-4b20-994b-bb8afe340087
Put Modifier.sky(sky) on the background you want to blur, then overlay a composable with Modifier.cloudy(sky = sky, …):
val sky = rememberSky()
Box(modifier = Modifier.fillMaxSize()) {
// 1) Source — the content captured for blur
LazyVerticalGrid(
columns = GridCells.Fixed(2),
modifier = Modifier.sky(sky),
) {
items(posters) { poster -> GridPosterItem(poster) }
}
// 2) Overlay — a frosted glass app bar that blurs the grid behind it
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.cloudy(
sky = sky,
radius = 20,
tint = MaterialTheme.colorScheme.surface.copy(alpha = 0.3f),
)
) {
Text("Frosted Glass App Bar")
}
}By default the blurred backdrop is clipped to a rectangle. Pass a shape so the blur follows rounded corners instead of leaving a hard rectangular box inside a rounded glass surface:
val barShape = RoundedCornerShape(36.dp)
Row(
modifier = Modifier
.clip(barShape)
.cloudy(sky = sky, radius = 24, shape = barShape)
.background(Color.White.copy(alpha = 0.15f)),
) {
// tabs
}Use the progressive parameter to fade the blur across the surface — ideal for scroll-edge fades or vignettes:
// Blur is strongest at the top and fades to clear toward the bottom
Modifier.cloudy(
sky = sky,
radius = 25,
progressive = CloudyProgressive.TopToBottom(),
)| Mode | Effect |
|---|---|
CloudyProgressive.None |
Uniform blur (default) |
CloudyProgressive.TopToBottom(start, end, easing) |
Blur fades from top → bottom |
CloudyProgressive.BottomToTop(start, end, easing) |
Blur fades from bottom → top |
CloudyProgressive.Edges(fadeDistance, easing) |
Blur at the edges, clear in the center (vignette) |
Progressive blur uses an AGSL shader on Android 33+ and Skia shaders on iOS/macOS/Desktop/WASM. On Android 32 and below it falls back to uniform blur.
The backdrop refreshes automatically while the background scrolls or animates, and parks at zero frames once it settles. When the background changes outside of a scroll/animation (for example, an image finishes loading or you swap content), call Sky.invalidate() to re-capture:
AsyncImage(
model = imageUrl,
modifier = Modifier.sky(sky),
onSuccess = { sky.invalidate() },
)For an animated change that lasts a known duration (e.g. a tab cross-fade), pass the duration so the blur tracks the whole transition instead of freezing once the short settle tail elapses:
LaunchedEffect(selectedTab) {
sky.invalidate(durationMillis = 240) // matches the cross-fade duration
}| Parameter | Default | Description |
|---|---|---|
sky |
– | The Sky holding the captured background (required). |
radius |
20 (CloudyDefaults.BACKGROUND_RADIUS) |
Blur radius in pixels. Must be non-negative. |
progressive |
CloudyProgressive.None |
Gradient blur configuration. |
tint |
Color.Transparent |
Optional color blended over the blurred backdrop. |
enabled |
true |
When false, the effect is disabled (renders nothing). |
cpuBlurEnabled |
false (CloudyDefaults.CPP_BLUR_ENABLED) |
Enable CPU blur on Android 30-; otherwise a scrim is shown. |
shape |
RectangleShape |
Shape the blurred backdrop is clipped to. |
onStateChanged |
{} |
Callback for CloudyState changes. |
| Platform | API Level | Implementation | Progressive Blur |
|---|---|---|---|
| Android | 33+ | AGSL RuntimeShader | Supported |
| Android | 31–32 | RenderEffect | Uniform only |
| Android | 30- | Bitmap + CPU (if cpuBlurEnabled) or scrim |
Uniform only |
| iOS / macOS / Desktop / WASM | – | Skia BlurEffect | Supported |
On Android 30 and below, CPU blur is disabled by default and a semi-transparent scrim (
CloudyState.Success.Scrim) is shown instead, following a performance-first approach. SetcpuBlurEnabled = trueto force CPU blur on those devices.
You can easily implement blur effect with Landscapist, which is a Jetpack Compose image loading library that fetches and displays network images with Glide, Coil, and Fresco. For more information, see the Transformation section.
Cloudy provides a Modifier.liquidGlass() that creates a realistic glass lens effect with SDF-based crisp edges, normal-based refraction, and chromatic dispersion.
Note: For blur effects, use Modifier.cloudy() separately. The two modifiers are designed to work independently, giving you full control over each effect.
| Platform | Implementation | Features |
|---|---|---|
| Android 33+ | RuntimeShader (AGSL) | Full effect |
| Android 32- | Fallback | Tint + edge + shape (no lens refraction) |
| iOS | Skia RuntimeEffect | Full effect |
| macOS | Skia RuntimeEffect | Full effect |
| Desktop (JVM) | Skia RuntimeEffect | Full effect |
| WASM | Skia RuntimeEffect | Full effect |
var lensCenter by remember { mutableStateOf(Offset(100f, 100f)) }
Box(
modifier = Modifier
.fillMaxSize()
.pointerInput(Unit) {
detectDragGestures { change, _ ->
lensCenter = change.position
}
}
.cloudy(radius = 15) // Use Cloudy for blur (independent)
.liquidGlass(lensCenter = lensCenter) // Lens distortion effect
) {
Image(
painter = painterResource(R.drawable.photo),
contentDescription = null,
modifier = Modifier.fillMaxSize()
)
}The liquidGlass() and cloudy() modifiers are independent and composable:
// Blur only
.cloudy(radius = 20)
// Lens effect only
.liquidGlass(lensCenter = lensCenter)
// Both effects combined
.cloudy(radius = 15)
.liquidGlass(lensCenter = lensCenter)This separation gives you full access to Cloudy's blur API (radius, onStateChanged, CloudyState) while using the liquid glass lens effect.
You can customize the liquid glass effect with various parameters:
.liquidGlass(
lensCenter = lensCenter,
lensSize = Size(350f, 350f), // Size of the glass lens
cornerRadius = 50f, // Rounded corners
refraction = 0.25f, // Distortion amount
curve = 0.25f, // Lens curvature strength
dispersion = 0.0f, // Chromatic dispersion (RGB separation)
saturation = 1.0f, // Color saturation
contrast = 1.0f, // Light/dark contrast
tint = Color.Transparent, // Optional color tint
edge = 0.2f, // Edge lighting width
)| Parameter | Default | Description | Fallback (Android 32-) |
|---|---|---|---|
lensCenter |
- | Center position of the glass lens (required) | Yes |
lensSize |
350x350 | Size of the lens in pixels | Yes |
cornerRadius |
50f | Corner radius for rounded rectangle shape | Yes |
refraction |
0.25f | Controls how much background distorts through lens | No (requires API 33+) |
curve |
0.25f | Controls how strongly lens curves at center vs edges | No (requires API 33+) |
dispersion |
0.0f | Chromatic dispersion/aberration intensity | No (requires API 33+) |
saturation |
1.0f | Color saturation (1.0 = normal) | Approximation |
contrast |
1.0f | Light/dark contrast (1.0 = normal) | Approximation |
tint |
Transparent | Optional color tint overlay | Yes |
edge |
0.2f | Edge lighting width (0 = none) | Yes (as stroke) |
enabled |
true | Enable/disable the effect | Yes |
Note: On Android 32 and below, the lens refraction effect is not available since it requires
RuntimeShader(API 33+). The fallback draws a visible lens shape with tint, edge lighting, and color adjustments. For blur effects, useModifier.cloudy()separately.
Support it by joining stargazers for this repository. ⭐
Also, follow me on GitHub for my next creations! 🤩
The liquid glass shader implementation was inspired by FletchMcKee/liquid.
Designed and developed by 2022 skydoves (Jaewoong Eum)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.