Custom Drawing with Canvas
Canvas, DrawScope & graphicsLayer
When no built-in component fits โ a ring chart, a signature pad, a custom progress arc โ you drop to the `Canvas` composable and issue drawing commands inside a `DrawScope`.
๐ Explanation
The `Canvas` composable hands you a `DrawScope`, a coordinate space in pixels where `size`, `center`, and helpers like `drawArc`/`drawCircle`/`drawPath` live. Because the lambda re-runs whenever its inputs change, animating `progress` (e.g. via `animateFloatAsState`) redraws the ring smoothly. `Stroke(cap = StrokeCap.Round)` controls the line style, and offsetting by half the stroke width keeps the thick arc fully inside the bounds instead of clipping at the edges.
Note: Code execution is not available for Android/Kotlin in the browser. Use Android Studio to run and test the app.
import androidx.compose.foundation.Canvasimport androidx.compose.runtime.Composableimport androidx.compose.ui.Modifierimport androidx.compose.ui.geometry.Offsetimport androidx.compose.ui.geometry.Sizeimport androidx.compose.ui.graphics.Colorimport androidx.compose.ui.graphics.StrokeCapimport androidx.compose.ui.graphics.drawscope.Strokeimport androidx.compose.ui.unit.dp@Composablefun ProgressRing(progress: Float, // 0f..1fmodifier: Modifier = Modifier,color: Color = Color(0xFF006A60),strokeWidth: Float = 24f) {Canvas(modifier = modifier.size(120.dp)) {// DrawScope exposes size, center, and draw* helpers in pixelsval stroke = Stroke(width = strokeWidth, cap = StrokeCap.Round)val arcSize = Size(size.width - strokeWidth, size.height - strokeWidth)val topLeft = Offset(strokeWidth / 2, strokeWidth / 2)// 1. Background trackdrawArc(color = color.copy(alpha = 0.15f),startAngle = -90f,sweepAngle = 360f,useCenter = false,topLeft = topLeft,size = arcSize,style = stroke)// 2. Foreground progress, swept from the topdrawArc(color = color,startAngle = -90f,sweepAngle = 360f * progress.coerceIn(0f, 1f),useCenter = false,topLeft = topLeft,size = arcSize,style = stroke)}}