Glow Search Box

Glow Search Box

Subscribe for Live Previews, in your browser

$3 / month

Code

import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.Icon
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Search
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.draw.dropShadow
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.center
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.drawscope.rotate
import androidx.compose.ui.graphics.layer.drawLayer
import androidx.compose.ui.graphics.rememberGraphicsLayer
import androidx.compose.ui.graphics.withSaveLayer
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import theme.Blue200
import theme.Blue300
import theme.Green200
import theme.Red200
import theme.Red400
import theme.Transparent
import theme.White
import theme.Yellow200
import theme.Yellow500
import theme.Zinc200
import theme.Zinc700


@Composable
fun GlowSearchBox() {
    var text by remember { mutableStateOf("") }
    BasicTextField(
        value = text,
        onValueChange = { text = it },
        modifier = Modifier.width(300.dp),
        cursorBrush = Brush.verticalGradient(
            colors = listOf(Zinc200, Zinc200)
        ),
        textStyle = LocalTextStyle.current.copy(
            color = Zinc200,
        ),
        decorationBox = { field ->
            val rotation by rememberInfiniteTransition().animateFloat(
                initialValue = 0f,
                targetValue = 360f,
                animationSpec = infiniteRepeatable(
                    animation = tween(
                        durationMillis = 3000,
                        easing = LinearEasing,
                    )
                )
            )

            BoxWithConstraints {
                MaskBox(
                    modifier = Modifier
                        .matchParentSize(),
                    overlay = {
                        rotate(
                            degrees = rotation
                        ) {
                            drawCircle(
                                brush = Brush.sweepGradient(
                                    colors = listOf(
                                        Transparent,
                                        Transparent,
                                        Red400,
                                        Yellow500,
                                        Green200,
                                        Blue300,
                                        Transparent,
                                    )
                                ),
                                radius = size.width * size.height,
                            )
                        }
                    },
                    content = {
                        Box(
                            modifier = Modifier
                                .matchParentSize()
                                .dropShadow(
                                    shape = CircleShape,
                                ) {
                                    alpha = .5f
                                    radius = 40f
                                    color = White
                                }
                                .dropShadow(
                                    shape = CircleShape,
                                ) {
                                    alpha = .2f
                                    spread = 50f
                                    radius = 400f
                                    color = White
                                }
                                .border(
                                    width = 1.dp,
                                    color = White,
                                    shape = CircleShape,
                                )
                        )

                    }
                )

                MaskBox(
                    modifier = Modifier
                        .zIndex(10f)
                        .matchParentSize(),
                    overlay = {
                        rotate(
                            degrees = rotation
                        ) {
                            drawCircle(
                                brush = Brush.sweepGradient(
                                    colors = listOf(
                                        Transparent,
                                        Transparent,
                                        Red200,
                                        Yellow200,
                                        Green200,
                                        Blue200,
                                        Transparent,
                                    )
                                ),
                                radius = size.width * size.height,
                            )
                        }

                    },
                    content = {
                        Box(
                            modifier = Modifier
                                .matchParentSize()
                                .border(
                                    width = 1.dp,
                                    color = White,
                                    shape = CircleShape,
                                )
                        )
                    }
                )

                Row(
                    modifier = Modifier
                        .width(300.dp)
                        .background(
                            color = Zinc700,
                            shape = CircleShape
                        )
                        .padding(12.dp),
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    Icon(
                        imageVector = Icons.Rounded.Search,
                        contentDescription = null,
                        tint = Zinc200,
                    )
                    Spacer(Modifier.width(12.dp))
                    field()
                }
            }
        }
    )
}

@Composable
private fun MaskBox(
    modifier: Modifier = Modifier,
    overlay: DrawScope.() -> Unit,
    content: @Composable BoxScope.() -> Unit,
) {
    val graphicsLayer = rememberGraphicsLayer()
    Box(
        modifier
            .drawWithContent {
                graphicsLayer.record {
                    this@drawWithContent.drawContent()
                }
                graphicsLayer.blendMode = BlendMode.DstIn
                layer(
                    bounds = Rect(
                        center = center,
                        radius = size.width * size.height,
                    )
                ) {
                    overlay()
                    drawLayer(graphicsLayer)
                }
            },
        content = content,
    )
}

private fun DrawScope.layer(
    bounds: Rect = Rect(size.center, size.width * size.height),
    block: DrawScope.() -> Unit
) =
    drawIntoCanvas { canvas ->
        canvas.withSaveLayer(
            bounds = bounds,
            paint = Paint(),
        ) { block() }
    }
Update: Replaced gradient recreation with a mask for better performance (As pointed out by Arty Bishop)
Mastodon