Back to Blog

Build an Animated Dashboard with Spring Physics | Kotlin Desktop #1

Sandy LaneSandy Lane

Video: Build an Animated Dashboard with Spring Physics | Kotlin Desktop #1 by Taught by Celeste AI - AI Coding Coach

Watch full page →

Build an Animated Dashboard with Spring Physics in Kotlin Desktop

Learn how to create a dynamic and visually appealing dashboard using Kotlin Compose Desktop by leveraging spring physics for natural animations. This example demonstrates animating values with animateXAsState, smooth color transitions, enter/exit animations with AnimatedVisibility, and continuous shimmer effects using rememberInfiniteTransition.

Code

import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Composable
fun AnimatedDashboard() {
  var selectedTab by remember { mutableStateOf(0) }

  // Animate tab content crossfade
  Crossfade(targetState = selectedTab) { tab ->
    when (tab) {
      0 -> StatCard(value = 75, label = "CPU Usage", color = Color(0xFF4CAF50))
      1 -> StatCard(value = 40, label = "Memory", color = Color(0xFF2196F3))
    }
  }

  Spacer(Modifier.height(16.dp))

  // Tab buttons
  Row {
    listOf("CPU", "Memory").forEachIndexed { index, title ->
      Button(onClick = { selectedTab = index }, Modifier.padding(4.dp)) {
        Text(title)
      }
    }
  }
}

@Composable
fun StatCard(value: Int, label: String, color: Color) {
  // Animate progress value with spring physics for bouncy effect
  val animatedValue by animateIntAsState(
    targetValue = value,
    animationSpec = spring(dampingRatio = Spring.DampingRatioMediumBouncy, stiffness = Spring.StiffnessLow)
  )

  // Animate color transition smoothly
  val animatedColor by animateColorAsState(targetValue = color)

  // Infinite shimmer effect for pulse
  val infiniteTransition = rememberInfiniteTransition()
  val pulseAlpha by infiniteTransition.animateFloat(
    initialValue = 0.3f,
    targetValue = 1f,
    animationSpec = infiniteRepeatable(
      animation = tween(1000, easing = FastOutSlowInEasing),
      repeatMode = RepeatMode.Reverse
    )
  )

  Card(
    shape = RoundedCornerShape(12.dp),
    modifier = Modifier.size(150.dp, 150.dp),
    colors = CardDefaults.cardColors(containerColor = animatedColor.copy(alpha = pulseAlpha))
  ) {
    Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
      // Draw progress ring
      Canvas(Modifier.size(100.dp)) {
        val sweepAngle = (animatedValue / 100f) * 360f
        drawArc(
          color = animatedColor,
          startAngle = -90f,
          sweepAngle = sweepAngle,
          useCenter = false,
          style = androidx.compose.ui.graphics.drawscope.Stroke(width = 12f)
        )
      }
      Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Text("$animatedValue%", style = MaterialTheme.typography.headlineMedium, color = Color.White)
        Text(label, style = MaterialTheme.typography.bodyMedium, color = Color.White.copy(alpha = 0.7f))
      }
    }
  }
}

Key Points

  • Use animateIntAsState with spring physics for natural, bouncy value animations.
  • animateColorAsState enables smooth color transitions for UI elements.
  • Crossfade provides seamless content switching between different dashboard tabs.
  • rememberInfiniteTransition creates continuous shimmer or pulsing animation effects.
  • Canvas.drawArc is useful for drawing animated progress rings representing dynamic data.