How to Create Animated Gender Sign in Jetpack Compose

Kappdev
4 min readAug 3, 2024

--

Welcome πŸ™‹

In this article, we’ll create a cool animated Gender Sign in Jetpack Compose using Canvas.

Let’s dive in πŸš€

Support Classes

Before we begin implementing the icon, we need two support classes.

First, we need an enum class GenderSign to represent the sign options:

enum class GenderSign { MALE, FEMALE }

Second, we need a data class GenderSignColors to represent the color states for the different options:

data class GenderSignColors(
val maleSignColor: Color = Color.Blue,
val femaleSignColor: Color = Color.Red
)

Defining the Function

Alright, now we can define the function:

@Composable
fun AnimatedGenderSign(
genderSign: GenderSign,
modifier: Modifier = Modifier,
strokeWidth: Dp = 2.dp,
strokeCap: StrokeCap = StrokeCap.Butt,
colors: GenderSignColors = GenderSignColors(),
animDuration: Int = 350
)

Parameters

🎯 genderSign πŸ‘‰ The current gender sign to display.

🎯 modifier πŸ‘‰ Modifier to be applied to the Canvas layout.

🎯 strokeWidth πŸ‘‰ Width of the stroke.

🎯 strokeCap πŸ‘‰ Style of the stroke ends.

🎯 colors πŸ‘‰ Colors for different sign options.

🎯 animDuration πŸ‘‰ Animation duration in milliseconds.

Implementation

Animations

First, we need to animate two values based on the genderSign parameter:

1️⃣ Progress Animation: This represents the overall progress of the animation, ranging from 0 to 1.

val progress by animateFloatAsState(
targetValue = when (genderSign) {
GenderSign.FEMALE -> 0f
GenderSign.MALE -> 1f
},
animationSpec = tween(animDuration)
)

2️⃣ Color Animation: This determines the color of the sign.

val color by animateColorAsState(
targetValue = when (genderSign) {
GenderSign.FEMALE -> colors.femaleSignColor
GenderSign.MALE -> colors.maleSignColor
},
animationSpec = tween(animDuration)
)

Canvas Drawing

Finally, we can draw the animated sign:

Canvas(modifier = modifier.size(24.dp)) {
// Get the width and height of the Canvas
val width = size.width
val height = size.height

// Calculate the radius of the circle and padding
val circleRadius = height * 0.24f
val padding = height * 0.1f

// Calculate the center of the circle with animation progress
val circleCenter = Offset(
x = width / 2,
y = circleRadius + padding + ((height / 2 - padding * 2) * progress)
)

// Create a Path for the gender sign
val genderSignPath = Path().apply {
// Add a circle to the Path
addOval(Rect(circleCenter, circleRadius))

// Add the core line
moveTo(x = circleCenter.x, y = circleCenter.y + circleRadius)
lineTo(x = circleCenter.x, y = circleCenter.y + circleRadius * 2)

// Add the lines for the arrowhead/cross
moveTo(x = circleCenter.x - circleRadius * 0.4f, y = circleCenter.y + circleRadius * 1.6f)
lineTo(x = circleCenter.x, y = circleCenter.y + circleRadius * 1.6f + (circleRadius * 0.4f * progress))
lineTo(x = circleCenter.x + circleRadius * 0.4f, y = circleCenter.y + circleRadius * 1.6f)
}

// Rotate and draw the Path
rotate(-135f * progress, circleCenter) {
drawPath(
path = genderSignPath,
color = color,
style = Stroke(
width = strokeWidth.toPx(),
cap = strokeCap
)
)
}
}
Transition

Congratulations πŸ₯³! We’ve successfully built it πŸ‘. You can find the full code on GitHub Gist πŸ§‘β€πŸ’». Let’s explore the usage πŸ‘‡

Practical Usage πŸ’β€β™‚οΈ

Clickable Utility

To make the code cleaner, let’s define the clickableGenderSign modifier:

fun Modifier.clickableGenderSign(
currentSign: GenderSign,
onSignChanged: (newSign: GenderSign) -> Unit
) = composed {
this.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = {
onSignChanged(
when (currentSign) {
GenderSign.MALE -> GenderSign.FEMALE
GenderSign.FEMALE -> GenderSign.MALE
}
)
}
)
}

Now, let’s see what we’ve built πŸš€

val (sign, updatedSign) = remember { mutableStateOf(GenderSign.FEMALE) }

AnimatedGenderSign(
genderSign = sign,
strokeCap = StrokeCap.Round,
strokeWidth = 16.dp,
modifier = Modifier
.size(200.dp)
.clickableGenderSign(sign, updatedSign)
)

Output ✨

You might also like πŸ‘‡

Thank you for reading this article! ❀️ If you found it enjoyable and valuable, show your appreciation by clapping πŸ‘ and following Kappdev for more exciting articles 😊

πŸ”” Subscribe to my πŸ‘‰ Email Notifications to stay updated with my latest content.

Happy coding!

--

--

Kappdev
Kappdev

Written by Kappdev

πŸ’‘ Curious Explorer 🧭 Kotlin and Compose enthusiast πŸ‘¨β€πŸ’» Passionate about self-development and growth ❀️‍πŸ”₯ Push your boundaries πŸš€