Build a Simple Windmill with Rotating Animation Jetpack Compose: A Beginner-Friendly Guide


If you’ve ever wanted to animate something in Jetpack Compose but didn’t know where to start, this post is for you. Today, we’re going to build a cute, animated windmill, where the blades spin continuously using Jetpack Compose’s animation APIs.
And don’t worry – we’ll break down every line of code so that even if you’re just starting out with Compose, you’ll feel right at home.
What We’re Building
A blue sky background 🌤️
A green land base 🌱
A static windmill body 🏗️
A rotating windmill blade (animation magic!) 🔄
Here’s a quick preview of what it’ll look like:
Resources:
Please find the required drawables here:
- blade.xml - This is the turbine blade that rotates
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="94dp"
android:height="94dp"
android:viewportWidth="94"
android:viewportHeight="94">
<path
android:pathData="M37.516,24.108L35.648,24.97L57.096,71.43L58.964,70.568L37.516,24.108Z"
android:fillColor="#C4AE9B"/>
<path
android:pathData="M28.937,0.007L22.867,2.811L36.452,32.225L42.522,29.422L28.937,0.007Z"
android:fillColor="#996641"/>
<path
android:pathData="M57.398,61.633L51.328,64.437L64.913,93.851L70.983,91.048L57.398,61.633Z"
android:fillColor="#996641"/>
<path
android:pathData="M28.937,0L25.902,1.402L39.487,30.816L42.522,29.415L28.937,0Z"
android:fillColor="#825434"/>
<path
android:pathData="M57.398,61.626L54.363,63.028L67.948,92.442L70.983,91.041L57.398,61.626Z"
android:fillColor="#825434"/>
<path
android:pathData="M70.546,34.932L24.086,56.379L24.948,58.247L71.408,36.799L70.546,34.932Z"
android:fillColor="#C4AE9B"/>
<path
android:pathData="M29.414,51.333L0,64.918L2.803,70.988L32.218,57.403L29.414,51.333Z"
android:fillColor="#996641"/>
<path
android:pathData="M91.047,22.868L61.633,36.453L64.436,42.523L93.851,28.938L91.047,22.868Z"
android:fillColor="#996641"/>
<path
android:pathData="M29.418,51.331L0.004,64.916L1.406,67.951L30.82,54.366L29.418,51.331Z"
android:fillColor="#825434"/>
<path
android:pathData="M91.043,22.868L61.629,36.453L63.03,39.487L92.445,25.902L91.043,22.868Z"
android:fillColor="#825434"/>
<path
android:pathData="M49.563,46.859C49.563,48.471 48.36,49.778 46.873,49.778C45.387,49.778 44.184,48.471 44.184,46.859C44.184,45.247 45.387,43.94 46.873,43.94C48.36,43.94 49.563,45.247 49.563,46.859Z"
android:fillColor="#996641"/>
</vector>
- body.xml - This is the windmill body
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="50dp"
android:height="80dp"
android:viewportWidth="50"
android:viewportHeight="80">
<path
android:pathData="M48.323,30.97H1.523V79.827H48.323V30.97Z"
android:fillColor="#996641"/>
<path
android:pathData="M24.923,0.884L1.523,30.97H48.323L24.923,0.884Z"
android:fillColor="#BF8C6A"/>
<path
android:pathData="M33.669,43.056H16.184V78.541H33.669V43.056Z"
android:fillColor="#453D39"/>
<path
android:pathData="M49.096,32.384C49.096,33.449 48.543,34.313 46.91,34.313H2.682C1.049,34.313 0.496,33.449 0.496,32.384C0.496,31.32 1.049,30.456 2.682,30.456H46.91C48.543,30.456 49.096,31.32 49.096,32.384Z"
android:fillColor="#C4AE9B"/>
</vector>
Okay now we have all that we want, Let’s dive in!
Step 1: Creating the Base Layout
We start with a Box that fills the whole screen and sets the background color to a sky blue.
Box(
modifier = Modifier
.fillMaxSize()
.background(color = SkyBlue),
contentAlignment = Alignment.Center
)
val SkyBlue = Color(0xFF96e2ee)
This gives us a full-screen canvas to build on.
Step 2: ConstraintLayout for Positioning
Inside the Box
, we use a ConstraintLayout
to position two main elements:
The land (green rectangle at the bottom)
The windmill (centered just above the land)
ConstraintLayout(
modifier = Modifier.fillMaxSize()
)
We define two references (land
, windMill
) and use constrainAs()
to position them relative to each other.
Step 3: Drawing the Land
This part is simple—we use a Spacer
to create a green strip at the bottom:
Spacer(
modifier = Modifier
.constrainAs(ref = land){
bottom.linkTo(parent.bottom)
}
.fillMaxWidth()
.height(50.dp)
.background(color = LandGreen)
)
val LandGreen = Color(0xFF66a558)
Think of this as the "ground" our windmill stands on.
Step 4: Adding the Windmill Body and Blades
Inside another ConstraintLayout
, we draw two images:
Body – the static part of the windmill
Blade – the part that rotates
Image(
modifier = Modifier
.constrainAs(ref = body) {},
painter = painterResource(id = R.drawable.body),
contentDescription = "Body"
)
The body stays still. It just gets centered above the land.
The blade, however, is where the fun begins.
Step 5: Rotating the Blades with Animation
We use rememberInfiniteTransition()
to create a smooth, repeating animation.
val infiniteTransition = rememberInfiniteTransition(label = "InfiniteTransition")
val turbineState = infiniteTransition.animateFloat(
initialValue = 0F,
targetValue = 360F,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = 2000,
easing = LinearEasing
)
),
label = "Turbine"
)
Here’s what’s happening:
We animate from 0° to 360° (a full circle)
It takes 2 seconds per rotation
It repeats forever
The animation uses LinearEasing (constant speed)
Then we apply the rotation to the blade image:
.rotate(degrees = turbineState.value)
Boom 💥 – your windmill blades now spin endlessly!
So, the entire WindMill()
composable will look like this:
// WindMill.kt
@Composable
fun WindMill() {
val infiniteTransition = rememberInfiniteTransition(label = "InfiniteTransition")
val turbineState = infiniteTransition.animateFloat(
initialValue = 0F,
targetValue = 360F,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = 2000,
easing = LinearEasing
)
),
label = "Turbine"
)
Box(
modifier = Modifier
.fillMaxSize()
.background(color = SkyBlue),
contentAlignment = Alignment.Center
) {
ConstraintLayout(
modifier = Modifier
.fillMaxSize()
) {
val (land, windMill) = createRefs()
Spacer(
modifier = Modifier
.constrainAs(ref = land){
bottom.linkTo(parent.bottom)
}
.fillMaxWidth()
.height(50.dp)
.background(color = LandGreen)
)
ConstraintLayout(
modifier = Modifier
.constrainAs(ref = windMill){
bottom.linkTo(land.top)
centerHorizontallyTo(land)
}
) {
val (blade, body) = createRefs()
Image(
modifier = Modifier
.constrainAs(ref = body) {},
painter = painterResource(id = R.drawable.body),
contentDescription = "Body"
)
Image(
modifier = Modifier
.constrainAs(ref = blade) {
centerHorizontallyTo(body)
centerAround(body.top)
}
.padding(top = 6.dp)
.rotate(degrees = turbineState.value),
painter = painterResource(id = R.drawable.blade),
contentDescription = "Blade"
)
}
}
}
}
And, Move the custom colors that we have added above in your Color.kt file
// Color.kt
val LandGreen = Color(0xFF66a558)
val SkyBlue = Color(0xFF96e2ee)
Final Thoughts
You’ve just built a complete animated component in Jetpack Compose using:
Layouts (
Box
,Spacer
,ConstraintLayout
)Images
Infinite animation
Modifiers for styling and rotation
This example is perfect for learning how to compose UIs declaratively and make them come alive with animation.
Want More?
Try experimenting with:
Different animation durations and easing
Multiple windmills!
Sun, clouds, or birds using
Canvas
or images
Feel free to copy this code and build your own animated farm. Compose is fun once you get the hang of it. Happy spinning! 🌪️
Subscribe to my newsletter
Read articles from Jai Keerthick directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
