Files
AndroidLiquidGlass/README.md
T

9.6 KiB

Liquid Glass

Apple's Liquid Glass effect for Android Jetpack Compose.

Demos

Screenshot of the catalog app Screenshots of the music player demo

Library

⚠️ The library is in alpha stage, every API may be changed, use it on your own risk.

Add to project

JitPack Release

// settings.gradle.kts in root project
dependencyResolutionManagement {
    repositories {
        maven("https://jitpack.io")
    }
}

// build.gradle.kts in module
implementation("com.github.Kyant0:AndroidLiquidGlass:<version>")

Examples

After 1.0.0-alpha10

Basic example

val backdrop = rememberLayerBackdrop(backgroundColor = Color.White)

Box {
    // backdrop content used to fill the backdrop layer
    Box(Modifier.backdrop(backdrop))

    // icon button with glass effect
    Box(
        Modifier
            .drawBackdrop(
                backdrop = backdrop,
                shapeProvider = { CircleShape },
                // draw a scrim to increase readability
                onDrawSurface = { drawRect(background.copy(alpha = 0.5f)) }
            ) {
                // saturation boost
                saturation()
                // blur
                blur(2f.dp.toPx())
                // glass effect
                refraction(height = 8f.dp.toPx(), amount = size.minDimension)
            }
            .clickable {}
            .size(48.dp),
        contentAlignment = Alignment.Center
    ) {}
}

Advanced example

Here is the full definition of the drawBackdrop modifier:

fun Modifier.drawBackdrop(
    backdrop: Backdrop,
    shapeProvider: () -> Shape,
    highlight: (() -> Highlight?)? = DefaultHighlight,
    shadow: (() -> Shadow?)? = DefaultShadow,
    onDrawBackdrop: DrawScope.(drawBackdrop: DrawScope.() -> Unit) -> Unit = { it() },
    onDrawSurface: (DrawScope.() -> Unit)? = null,
    effects: BackdropEffectScope.() -> Unit
): Modifier

The following example shows how to draw a dynamic highlight and apply scaling to the backdrop content:

val backdrop = rememberLayerBackdrop(backgroundColor = Color.White)

Box {
    // backdrop content used to fill the backdrop layer
    Box(Modifier.backdrop(backdrop))

    // [optional] scale if needed
    val originalScale = 1.2f

    // the highlight angle in degrees produced by sensors
    val highlightAngle = 45f

    // icon button with glass effect
    Box(
        Modifier
            // [optional] scale if needed
            .graphicsLayer {
                scaleX = originalScale
                scaleY = originalScale
            }
            .drawBackdrop(
                backdrop = backdrop,
                shapeProvider = { CircleShape },
                // the dynamic highlight
                highlight = { Highlight { HighlightStyle.Dynamic(angle = highlightAngle) } },
                // the backdrop content to draw with effects
                onDrawBackdrop = { drawBackdrop ->
                    // apply the inverse scale to the backdrop content
                    scale(1f / originalScale, 1f / originalScale, Offset.Zero) {
                        drawBackdrop()
                    }
                    // [optional] call `drawImage()` here or similar to draw additional content to the front of backdrop layer
                },
                // the surface content to draw above the backdrop layer and below the highlight layer
                onDrawSurface = { drawRect(background.copy(alpha = 0.5f)) }
            )
            .clickable {}
            .size(48.dp),
        contentAlignment = Alignment.Center
    ) {}
}

1.0.0-alpha09

Basic example

val backdrop = rememberLayerBackdrop(backgroundColor = Color.White)

Box {
    // backdrop content used to fill the backdrop layer
    Box(Modifier.backdrop(backdrop))

    // icon button with glass effect
    Box(
        Modifier
            // [optional] drop shadow effect
            .backdropShadow(CircleShape)
            // draw the backdrop with glass effect
            .drawBackdrop(backdrop) {
                // set the shape
                shape = CircleShape

                // apply visual effects
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                    saturation()
                    blur(2f.dp)
                    // here is the glass effect
                    refraction(height = 8f.dp.toPx(), amount = size.minDimension)
                }

                // [optional] draw a brilliant highlight
                drawHighlight()
            }
            .clickable {}
            .size(48.dp),
        contentAlignment = Alignment.Center
    ) {}
}

Advanced example

val backdrop = rememberLayerBackdrop(backgroundColor = Color.White)

Box {
    // backdrop content used to fill the backdrop layer
    Box(Modifier.backdrop(backdrop))

    // [optional] scale if needed
    val originalScale = 1f

    // icon button with glass effect
    Box(
        Modifier
            // [optional] scale if needed
            .graphicsLayer {
                scaleX = originalScale
                scaleY = originalScale
            }
            // [optional] drop shadow effect
            .backdropShadow(CircleShape)
            // draw the backdrop with glass effect
            .drawBackdrop(backdrop) {
                // set the shape
                shape = CircleShape

                // apply visual effects
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                    saturation()
                    blur(2f.dp)
                    // here is the glass effect with dispersion
                    refractionWithDispersion(height = 8f.dp.toPx(), amount = size.minDimension)
                }

                // [optional] draw custom content on the backdrop layer
                onDrawBackdrop { drawBackdrop ->
                    // apply the inverse scale to the backdrop content
                    scale(1f / originalScale, 1f / originalScale, Offset.Zero) {
                        drawBackdrop()
                    }
                }

                // [optional] draw a scrim with a brilliant highlight
                onDrawSurfaceWithHighlight { drawRect(Color.White.copy(alpha = 0.3f)) }
            }
            .clickable {}
            .size(48.dp),
        contentAlignment = Alignment.Center
    ) {}
}

Before 1.0.0-alpha09 (exclusive)

val providerState = rememberLiquidGlassProviderState(
    // if the providing content has any transparent area and there is a background behind the content, set the
    // background color here, or set it to null
    backgroundColor = Color.White
)

// the content behind the glass
Box(Modifier.liquidGlassProvider(providerState))

// glass
Box(
    Modifier.liquidGlass(
        providerState,
        GlassStyle(
            shape = RoundedCornerShape(16f.dp),
            // you can customize more properties
            innerRefraction = InnerRefraction(
                height = RefractionHeight(8f.dp),
                amount = RefractionAmount((-16f).dp),
                depthEffect = 0f // or `1f` to have more 3D effect
            ),
            dispersion = Dispersion.None, // or `Dispersion.Automatic` to enable dispersion effect
            material = GlassMaterial(
                blurRadius = 2f.dp,
                brush = SolidColor(Color.White),
                alpha = 0.3f
            ),
            highlight = GlassHighlight.Default,
            shadow = GlassShadow.Default // or `null` to disable shadow
        )
    )
)

Use the block modifier variant to improve performance when the style changes frequently:

val progress by animateFloatAsState(if (isPressed) 1f else 0f)

Box(
    Modifier.liquidGlass(providerState) {
        GlassStyle(
            shape = RoundedCornerShape(16f.dp),
            innerRefraction = InnerRefraction(
                height = RefractionHeight(8f.dp * progress),
                amount = RefractionAmount((-16f).dp * progress)
            ),
            material = GlassMaterial(
                blurRadius = 2f.dp * progress,
                brush = SolidColor(Color.White),
                alpha = 0.3f * progress
            )
        )
    }
)

Comparing with iOS

iOS device: iPhone 16 Pro Max (emulator), using GlassExplorer

Android device: Google Pixel 4 XL (the smallest width is adjusted to 440 dp to match the density of the iOS device)

Glass size: 300 x 300, corner radius: 30

iOS Android

Complete comparisons:

Star history

Star history chart