Canvas in Jetpack Compose
Why Use Canvas in Jetpack Compose?
- ✅ Full Control – Draw anything pixel-perfect.
- ✅ Lightweight & Efficient – Avoids unnecessary recompositions.
- ✅ Custom Animations – Beyond standard UI animations.
- ✅ Interactive UI – Gestures, touch-based interactions.
- ✅ Optimized Performance – Hardware-accelerated rendering.
Basic Drawing Functions
- drawRect() – Draws a rectangle.
- drawRoundRect() – Draws a rectangle with rounded corners.
- drawCircle() – Draws a circle.
- drawOval() – Draws an oval.
- drawArc() – Draws an arc.
- drawLine() – Draws a straight line between two points.
- drawPoints() – Draws multiple points.
- drawPath() – Draws a custom path.
- drawImage() – Draws an image on the canvas.
- drawText() – Draws text (not directly available; use androidx.compose.ui.graphics.NativeCanvas).
How to Position an object on Screen in Jetpack Compose Canvas
| Position | Offset Formula |
|---|---|
| Center | Offset(size.width / 2, size.height / 2) |
| Top-Left | Offset(radius, radius) |
| Top-Right | Offset(size.width - radius, radius) |
| Bottom-Left | Offset(radius, size.height - radius) |
| Bottom-Right | Offset(size.width - radius, size.height - radius) |
| Custom | Offset(x, y) |
@Composable
fun CanvasDemoScreen() {
val imageBitmap = ImageBitmap.imageResource(R.drawable.image)
Canvas(modifier = Modifier.fillMaxSize()) {
drawImage(
image = imageBitmap,
topLeft = Offset(70f, 150f)
)
}
}1. Basic Drawing Functions
package com.example.jetpackskill
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
@Composable
fun CanvasDemoScreen() {
Canvas(modifier = Modifier.fillMaxSize()) {
drawCircle(
color = Color.Red,
radius = 100f,
center = Offset(size.width / 2, size.height / 2)
)
}
}OUTPUT:
- size: Represents the dimensions of the canvas.
- Offset(x, y): Defines a position on the canvas.
- drawCircle(), drawRect(), drawLine(), etc.: Functions to draw basic shapes.
2. Drawing Advanced Shapes
package com.example.jetpackskill
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
@Composable
fun CanvasDemoScreen() {
Canvas(modifier = Modifier.fillMaxSize()) {
val path = Path().apply {
moveTo(100f, 100f)
lineTo(300f, 100f)
lineTo(200f, 300f)
close()
}
drawPath(path, color = Color.Blue)
}
}OUTPUT:
- Path.moveTo(x, y): Moves the starting point of the path.
- lineTo(x, y): Draws a line from the current point to (x, y).
- close(): Closes the shape, connecting the last point to the first.
3. Applying Transformations (Translate, Rotate, Scale)
package com.example.jetpackskill
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.rotate
@Composable
fun CanvasDemoScreen() {
Canvas(modifier = Modifier.fillMaxSize()) {
rotate(45f, pivot = Offset(size.width / 4, size.height / 2)) {
drawRect(Color.Green, size = Size(200f, 200f))
}
}
}OUTPUT:
- rotate(angle, pivot): Rotates the content.
- translate(x, y): Moves the drawing position.
- scale(factorX, factorY): Scales the drawing.
4. Animating Canvas Drawings
package com.example.jetpackskill
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
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.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@Composable
fun CanvasDemoScreen() {
val infiniteTransition = rememberInfiniteTransition()
val animatedRadius by infiniteTransition.animateFloat(
initialValue = 50f,
targetValue = 150f,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 1000,
easing = LinearEasing),
repeatMode = RepeatMode.Reverse
)
)
Canvas(modifier = Modifier.fillMaxSize()) {
drawCircle(Color.Magenta, radius = animatedRadius, center = center)
}
}- rememberInfiniteTransition(): Keeps the animation running.
- animateFloat(): Animates a float property.
- infiniteRepeatable(): Loops the animation infinitely.
5. Handling Touch Input in Canvas
package com.example.jetpackskill
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.fillMaxSize
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.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
@Composable
fun CanvasDemoScreen() {
var position by remember { mutableStateOf(Offset(200f, 200f)) }
Canvas(modifier = Modifier
.fillMaxSize()
.pointerInput(Unit) {
detectTapGestures { tapOffset -> position = tapOffset }
}
) {
drawCircle(Color.Cyan, radius = 50f, center = position)
}
}- detectTapGestures { tapOffset }: Detects tap events.
- utableStateOf(Offset()): Stores the tap position.
Output:
Lets draw text in Canvas using gradient brush
- Canvas Composable: The drawText function is part of DrawScope, which is available inside the Canvas composable.
- textMeasurer.measure(...): This measures the text before drawing it.
- drawText(...): This is called inside the Canvas drawing scope.
- Bigger and bolder text: (40.sp, FontWeight.Bold).
- White text with a black shadow: for better visibility.
- Gradient color effect using Brush.linearGradient.
- Blurred shadow: for a cool glowing effect.
package com.example.jetpackskill
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.unit.sp
@Composable
fun CanvasScreenDemo(modifier: Modifier = Modifier) {
val textMeasurer = rememberTextMeasurer()
Canvas(modifier = modifier.fillMaxSize()) {
val textLayoutResult = textMeasurer.measure(
text = "Hello, Canvas!",
style = TextStyle(
fontSize = 40.sp,
fontWeight = FontWeight.Bold,
color = Color.White,
shadow = Shadow(
color = Color.Black, // Shadow color
offset = Offset(4f, 4f), // Shadow offset
blurRadius = 8f // Shadow blur
)
)
)
// Create a gradient effect using a linear gradient shader
val gradientBrush = Brush.linearGradient(
colors = listOf(Color.Magenta, Color.Cyan, Color.Blue),
start = Offset.Zero,
end = Offset(300f, 0f)
)
drawText(
textLayoutResult,
topLeft = Offset(100f, 200f),
brush = gradientBrush // Apply the gradient color
)
}
}





