Icons
Iconsanimated

Download to tray

An arrow that drops into a tray, which then fills to confirm the download.

Installation

caveui components are copy-paste Jetpack Compose built entirely on Material 3 — there's no caveui dependency to add. Make sure Material 3 is on your classpath (it ships with the Compose BOM), then copy the Usage snippet below into your project.

kotlin
// build.gradle.kts (module)
dependencies {
    implementation(platform("androidx.compose:compose-bom:2025.06.00"))
    implementation("androidx.compose.material3:material3")
}

Usage

kotlin
@Composable
fun DownloadToTray(progress: Float) { // 0f..1f
    val color = LocalContentColor.current
    Canvas(Modifier.size(40.dp)) {
        val cx = size.width / 2
        val drop = (progress / 0.5f).coerceIn(0f, 1f)
        // Arrow descends in the first half.
        translate(top = lerp(0f, size.height * 0.18f, drop)) {
            drawLine(color, Offset(cx, size.height * 0.18f), Offset(cx, size.height * 0.55f), 3.dp.toPx(), StrokeCap.Round)
            drawPath(
                Path().apply {
                    moveTo(cx - 6.dp.toPx(), size.height * 0.42f)
                    lineTo(cx, size.height * 0.58f); lineTo(cx + 6.dp.toPx(), size.height * 0.42f)
                },
                color, style = Stroke(3.dp.toPx(), cap = StrokeCap.Round, join = StrokeJoin.Round),
            )
        }
        // Tray fills in the second half.
        val fill = ((progress - 0.5f) / 0.5f).coerceIn(0f, 1f)
        val trayTop = size.height * 0.72f
        drawRect(color.copy(alpha = 0.25f), Offset(size.width * 0.2f, trayTop), Size(size.width * 0.6f, size.height * 0.18f * fill))
        drawLine(color, Offset(size.width * 0.2f, size.height * 0.9f), Offset(size.width * 0.8f, size.height * 0.9f), 3.dp.toPx(), StrokeCap.Round)
    }
}