1. Bouncing Ball Animation
@Composable
fun BouncingBallAnimation() {
val ballPosition = remember { Animatable(0f) }
val ballSize = 50.dp
LaunchedEffect(Unit) {
ballPosition.animateTo(
targetValue = 600f,
animationSpec = infiniteRepeatable(
animation = tween(1000, easing = LinearEasing),
repeatMode = RepeatMode.Reverse
)
)
}
Canvas(modifier = Modifier.fillMaxSize()) {
drawCircle(
color = Color.Red,
radius = ballSize.toPx() / 2,
center = Offset(size.width / 2, ballPosition.value)
)
}
}2. Fade-in Text Animation
@Composable
fun FadeInTextAnimation() {
var alpha by remember { mutableFloatStateOf(0f) }
LaunchedEffect(Unit) {
alpha = 1f
}
val alphaAnim by animateFloatAsState(
targetValue = alpha,
animationSpec = tween(durationMillis = 2000), label = ""
)
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(
text = "Hello, Compose!",
fontSize = 32.sp,
color = Color.Black.copy(alpha = alphaAnim),
fontWeight = FontWeight.Bold
)
}
}3. Expandable Cards Animation
@Composable
fun ExpandableCardAnimation() {
var expanded by remember { mutableStateOf(false) }
val cardHeight by animateDpAsState(
targetValue = if (expanded) 200.dp else 100.dp, label = ""
)
Card(
modifier = Modifier
.padding(16.dp)
.height(cardHeight)
.fillMaxWidth()
.clickable { expanded = !expanded },
shape = RoundedCornerShape(8.dp)
) {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(text = if (expanded) "Expanded" else "Collapsed")
}
}
}4. Button Ripple Effect
@Composable
fun RippleEffectButton() {
Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Surface(
modifier = Modifier
.wrapContentSize()
.clickable { /* Clicked */ },
shape = MaterialTheme.shapes.medium,
color = MaterialTheme.colorScheme.primary,
shadowElevation = 8.dp
) {
Text(
text = "Ripple Button",
modifier = Modifier
.padding(16.dp),
color = MaterialTheme.colorScheme.onPrimary
)
}
}
}5. Swipe to Delete with Animation
@Composable
fun SwipeToDeleteAnimation() {
var isDeleted by remember { mutableStateOf(false) }
if (!isDeleted) {
Box(
modifier = Modifier
.fillMaxWidth()
.background(Color.LightGray)
.height(80.dp)
.pointerInput(Unit) {
detectHorizontalDragGestures { _, dragAmount ->
if (dragAmount > 300) {
isDeleted = true
}
}
},
contentAlignment = Alignment.Center
) {
BasicText(text = "Swipe to delete")
}
}
}Note:
MainActivity
package com.example.jetpackcomposeskill
import ...
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
JetpackComposeSkillTheme {
AnimationDemo()
}
}
}
}
}
AnimationDemo
package com.example.jetpackcomposeskill
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.foundation.Canvas
import androidx.compose.ui.unit.dp
import androidx.compose.ui.geometry.Offset
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.tween
import androidx.compose.foundation.clickable
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.text.BasicText
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.input.pointer.pointerInput
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@Composable
fun AnimationDemo() {
val nav = rememberNavController()
NavHost(navController = nav, startDestination = "animDemo") {
composable("animDemo"){
AnimationDemo(navController = nav)
}
composable("bounce"){
BouncingBallAnimation()
}
composable("fade"){
FadeInTextAnimation()
}
composable("expand") {
ExpandableCardAnimation()
}
composable("ripple"){
RippleEffectButton()
}
composable("swipe"){
SwipeToDeleteAnimation()
}
}
}
@Composable
fun AnimationDemo(navController: NavController) {
Column(Modifier.fillMaxSize()){
Text("Bounce",
Modifier.padding(20.dp).clickable{navController.navigate("bounce")}, fontSize = 32.sp)
Text("Fade",
Modifier.padding(20.dp).clickable{navController.navigate("fade")}, fontSize = 32.sp)
Text("Expand",
Modifier.padding(20.dp).clickable{navController.navigate("expand")}, fontSize = 32.sp)
Text("Ripple",
Modifier.padding(20.dp).clickable{navController.navigate("ripple")}, fontSize = 32.sp)
Spacer(modifier = Modifier.width(20.dp))
Text("Swipe Me!",
Modifier.padding(20.dp).clickable{navController.navigate("swipe")}, fontSize = 32.sp)
}
}
@Composable
fun BouncingBallAnimation() {
val ballPosition = remember { Animatable(0f) }
val ballSize = 50.dp
LaunchedEffect(Unit) {
ballPosition.animateTo(
targetValue = 600f,
animationSpec = infiniteRepeatable(
animation = tween(1000, easing = LinearEasing),
repeatMode = RepeatMode.Reverse
)
)
}
Canvas(modifier = Modifier.fillMaxSize()) {
drawCircle(
color = Color.Red,
radius = ballSize.toPx() / 2,
center = Offset(size.width / 2, ballPosition.value)
)
}
}
@Composable
fun FadeInTextAnimation() {
var alpha by remember { mutableFloatStateOf(0f) }
LaunchedEffect(Unit) {
alpha = 1f
}
val alphaAnim by animateFloatAsState(
targetValue = alpha,
animationSpec = tween(durationMillis = 2000), label = ""
)
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(
text = "Hello, Compose!",
fontSize = 32.sp,
color = Color.Black.copy(alpha = alphaAnim),
fontWeight = FontWeight.Bold
)
}
}
@Composable
fun ExpandableCardAnimation() {
var expanded by remember { mutableStateOf(false) }
val cardHeight by animateDpAsState(
targetValue = if (expanded) 200.dp else 100.dp, label = ""
)
Card(
modifier = Modifier
.padding(16.dp)
.height(cardHeight)
.fillMaxWidth()
.clickable { expanded = !expanded },
shape = RoundedCornerShape(8.dp),
// backgroundColor = Color.LightGray
) {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(text = if (expanded) "Expanded" else "Collapsed")
}
}
}
@Composable
fun RippleEffectButton() {
Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Surface(
modifier = Modifier
.wrapContentSize()
.clickable { /* Clicked */ },
shape = MaterialTheme.shapes.medium,
color = MaterialTheme.colorScheme.primary,
shadowElevation = 8.dp
) {
Text(
text = "Ripple Button",
modifier = Modifier
.padding(16.dp),
color = MaterialTheme.colorScheme.onPrimary
)
}
}
}
@Composable
fun SwipeToDeleteAnimation() {
var isDeleted by remember { mutableStateOf(false) }
if (!isDeleted) {
Box(
modifier = Modifier
.fillMaxWidth()
.background(Color.LightGray)
.height(80.dp)
.pointerInput(Unit) {
detectHorizontalDragGestures { _, dragAmount ->
if (dragAmount > 300) {
isDeleted = true
}
}
},
contentAlignment = Alignment.Center
) {
BasicText(text = "Swipe to delete")
}
}
}
