How to Draw & Animate a Beating Heart in Jetpack Compose

How to Draw & Animate a Beating Heart in Jetpack Compose

How to Draw & Animate a Beating Heart in Jetpack Compose

Heart often refers to the stylized representation of the human heart, usually depicted as a symmetrical shape with a pointed bottom and rounded top, commonly associated with love, emotions, or symbols of affection. It’s frequently used in various forms of art, such as:
  1. Symbolism: Representing love, care, passion, and affection.
  2. Illustration: Often used in comics, greeting cards, tattoos, and other personal art expressions.
  3. Artistic Style: Can be drawn realistically to resemble an anatomical heart or in a simplified, cartoonish form, or even as an abstract design.
In many cultures and media, the heart symbol is universally understood as a representation of emotional or romantic feelings. Would you like tips on how to draw a heart, or any specific style of heart drawing?

How to build Sliding Puzzle 15 Game in Jetpack Compose?

sliding puzzle 15 game

Learning to draw a heart using Jetpack Compose—especially with cubic Bézier curvesis surprisingly useful in app development for several reasons:

✅ 1. Sharpens Your Canvas & Path Skills

  • Drawing a heart involves using Canvas, Path, and curve logic.
  • These are core for custom UI, animations, charts, games, and dynamic effects.

✅ 2. Real-World UI Use Cases

  • Heart icons are everywhere: likes, favorites, reaction buttons.
  • Creating custom hearts lets you animate, morph, or stylize them (e.g., glowing heart when liked).

✅ 3. Foundation for Drawing Anything Custom

  • If you understand how to draw a heart with Bézier curves, you can draw stars, waves, logos, or even characters.
  • It builds your skills for canvas-based apps, like drawing tools, diagram editors, or design apps.

✅ 4. Helps You in Interviews & Challenges

  • Many interviews for Android UI roles ask for things like: “Draw a custom shape using Compose without any image.”
  • Knowing Bézier curves and Canvas will help you stand out.
To draw a heart shape in Jetpack Compose, you can use the Canvas composable, which allows for custom drawing using a variety of shapes, including a heart.

Here's an example of how to draw a heart shape using Canvas in Jetpack Compose:
@Composable
fun RedHeartShape() {
Canvas(modifier = Modifier
.fillMaxSize()
.systemBarsPadding()) {

val canvasWidth = size.width
val canvasHeight = size.height

// Use center-based coordinates
val path = Path().apply {
moveTo(0f, 100f)

cubicTo(100f, -100f, 200f, 100f, 0f, 300f)
cubicTo(-200f, 100f, -100f, -100f, 0f, 100f)
}

withTransform({
translate(left = canvasWidth / 2, top = canvasHeight / 2 - 150f) // center heart
}) {
drawPath(path, Color.Red, style = Fill)
drawPath(path, Color.Black, style = Stroke(width = 8f, cap = StrokeCap.Round))
}
}
}
  • Canvas: The Canvas composable is used to draw custom shapes and paths.
  • Path: A Path object is used to define the shape of the heart using cubic Bézier curves.
  • moveTo: Moves the pen to a specific coordinate.
  • cubicTo: Draws a cubic Bézier curve between points.
  • drawPath: This draws the heart path with a red color and a stroke of 5 pixels width.

Result:

How to Draw Heart in Jetpack Compose

How to make Red Heart Interactive

To make

@Composable
fun BeatingHeart() {
// Infinite scaling animation for heartbeat
val scale = rememberInfiniteTransition().animateFloat(
initialValue = 1f,
targetValue = 1.2f,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 500,
easing = LinearEasing),
repeatMode = RepeatMode.Reverse
)
)

Box(
modifier = Modifier
.systemBarsPadding()
.fillMaxSize()
.graphicsLayer(
scaleX = scale.value,
scaleY = scale.value
)
) {
Canvas(modifier = Modifier
.fillMaxSize()
.systemBarsPadding()) {

val canvasWidth = size.width
val canvasHeight = size.height

// Use center-based coordinates
val path = Path().apply {
moveTo(0f, 100f)

cubicTo(100f, -100f, 200f, 100f, 0f, 300f)
cubicTo(-200f, 100f, -100f, -100f, 0f, 100f)
}

withTransform({
translate(left = canvasWidth / 2, top = canvasHeight / 2 - 150f) // center heart
}) {
drawPath(path, Color.Red, style = Fill)
drawPath(path, Color.Black, style = Stroke(width = 8f, cap = StrokeCap.Round))
}
}
}
}

Beating Heart in Jetpack Compose

How to build Animated heart drawing

Draw an animated heart shape using Jetpack Compose's Canvas, Path, and Animations.

  1. Composable function—used in Jetpack Compose to create UI. This one draws an animated heart.
  2. LaunchedEffect(Unit) runs once when the Composable is first launched.
  3. animateTo(1f) animates the value from 0 to 1 over 5 seconds (5000 ms).
  4. LinearEasing makes the animation smooth and consistent in speed.
  5. Canvas gives us a drawing space.
  6. Path is like a pen drawing lines and curves.
  7. moveTo moves to the bottom center of the heart.
  8. Two cubicTo() functions draw the right and left curves of the heart using Bezier curves.
  9. We use PathMeasure to find out the length of the heart path.
  10. Then getSegment() gives us only the portion of the path based on animation progress.
  11. So if pathProgress = 0.5, we draw half of the heart.

@Composable
fun AnimatedHeartDrawing() {
val pathProgress = remember { Animatable(0f) }
Box(
Modifier.fillMaxSize().systemBarsPadding(),
contentAlignment = Alignment.TopStart
) {
LaunchedEffect(Unit) {
pathProgress.animateTo(
targetValue = 1f,
animationSpec = tween(durationMillis = 5000,
easing = LinearEasing)
)
}

Canvas(
modifier = Modifier
.fillMaxWidth()
.height(500.dp)
.padding(32.dp)
) {

val path = Path().apply {
val width = size.width
val height = size.height
moveTo(width / 2, height / 5 * 3)

cubicTo(
width / 2 + width / 4, height / 5,
width / 2 + width / 2.5f, height / 5 * 2.5f,
width / 2, height
)

cubicTo(
width / 2 - width / 2.5f, height / 5 * 2.5f,
width / 2 - width / 4, height / 5,
width / 2, height / 5 * 3
)
}

val androidPath = path.asAndroidPath()
val pathMeasure = android.graphics.PathMeasure(androidPath, false)
val dst = android.graphics.Path()

// Animate in reverse: from end (length) down to 0
val start = pathMeasure.length * (1 - pathProgress.value)
val end = pathMeasure.length
pathMeasure.getSegment(start, end, dst, true)

drawPath(
path = Path().apply { addPath(dst.asComposePath()) },
color = Color.Red,
style = Stroke(width = 12f, cap = StrokeCap.Round)
)
}
}
}
Animated heart drawing

Learn  more about Cubic Bezier

Previous Post Next Post

Contact Form