Kotlin Desktop: Canvas, Path & Pointer Input Drawing App | Lesson 11
Video: Kotlin Desktop: Canvas, Path & Pointer Input Drawing App | Lesson 11 by Taught by Celeste AI - AI Coding Coach
Watch full page →Kotlin Desktop Drawing App: Canvas, Path & Pointer Input
Learn how to create a freehand drawing application using Kotlin Compose Desktop by leveraging the Canvas composable and Path API. This example demonstrates capturing pointer input gestures to draw smooth lines with customizable stroke width and color, along with undo and clear functionality.
Code
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Button
import androidx.compose.material.Slider
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.*
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.dp
// Data class representing a single freehand stroke
data class DrawingStroke(
val color: Color,
val strokeWidth: Float,
val points: MutableList = mutableListOf()
)
@Composable
@Preview
fun DrawingApp() {
// State holding all strokes drawn
val strokes = remember { mutableStateListOf() }
// Current stroke color and width
var currentColor by remember { mutableStateOf(Color.Black) }
var currentStrokeWidth by remember { mutableStateOf(4f) }
Column(modifier = Modifier.fillMaxSize().background(Color(0xFFFAFAFA)).padding(16.dp)) {
// Toolbar with color palette, stroke width slider, undo and clear buttons
Row(verticalAlignment = androidx.compose.ui.Alignment.CenterVertically) {
val colors = listOf(Color.Black, Color.Red, Color.Green, Color.Blue, Color.Magenta)
colors.forEach { color ->
Box(
modifier = Modifier
.size(36.dp)
.background(color, shape = CircleShape)
.border(
width = if (color == currentColor) 3.dp else 1.dp,
color = if (color == currentColor) Color.DarkGray else Color.LightGray,
shape = CircleShape
)
.padding(4.dp)
.clickable { currentColor = color }
)
Spacer(modifier = Modifier.width(8.dp))
}
Spacer(modifier = Modifier.width(16.dp))
Text("Stroke Width")
Slider(
value = currentStrokeWidth,
onValueChange = { currentStrokeWidth = it },
valueRange = 1f..20f,
modifier = Modifier.width(150.dp).padding(horizontal = 8.dp)
)
Spacer(modifier = Modifier.width(16.dp))
Button(onClick = { if (strokes.isNotEmpty()) strokes.removeLast() }) {
Text("Undo")
}
Spacer(modifier = Modifier.width(8.dp))
Button(onClick = { strokes.clear() }) {
Text("Clear")
}
}
Spacer(modifier = Modifier.height(16.dp))
// Drawing canvas
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.White)
.pointerInput(Unit) {
detectDragGestures(
onDragStart = { offset ->
// Start a new stroke with initial point
strokes.add(DrawingStroke(currentColor, currentStrokeWidth, mutableListOf(offset)))
},
onDrag = { change, _ ->
// Append points to the latest stroke as user drags
strokes.lastOrNull()?.points?.add(change.position)
change.consume()
}
)
}
) {
Canvas(modifier = Modifier.fillMaxSize()) {
strokes.forEach { stroke ->
val path = Path().apply {
if (stroke.points.isNotEmpty()) {
moveTo(stroke.points.first().x, stroke.points.first().y)
stroke.points.drop(1).forEach { point ->
lineTo(point.x, point.y)
}
}
}
drawPath(
path = path,
color = stroke.color,
style = Stroke(
width = stroke.strokeWidth,
cap = StrokeCap.Round,
join = StrokeJoin.Round
)
)
}
}
}
}
}
Key Points
- Use the Canvas composable with the DrawScope to render custom 2D paths representing freehand strokes.
- Build smooth lines by constructing Paths with moveTo and lineTo from recorded pointer points.
- Capture pointer input gestures using pointerInput and detectDragGestures to track user drawing.
- Manage strokes reactively with mutableStateListOf to update the UI as new points are added.
- Enhance UX with a color palette, adjustable stroke width slider, and undo/clear buttons for editing.