What You Will Build
A card that flips around its X axis with a 3D perspective when tapped, revealing different content on the back. The flip uses graphicsLayer with rotationX and a camera distance setting to create a convincing 3D effect. This pattern is used for flashcard apps, profile cards, and info-reveal interactions.
Why This Pattern Matters
3D card flips add a spatial dimension to flat interfaces. The key challenge is rendering the correct face based on rotation angle and counter-rotating the back face so its text reads correctly. Compose's graphicsLayer makes this straightforward.
The Flip Card Implementation
@Composable
fun FlipCardScreen() {
val cards = listOf(
Triple("Creativity", "Think outside the box", Color(0xFF6366F1)),
Triple("Innovation", "Build the future today", Color(0xFFEC4899)),
Triple("Excellence", "Pursue perfection always", Color(0xFF10B981))
)
Column(
modifier = Modifier.fillMaxSize().padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
cards.forEach { (front, back, color) ->
var flipped by remember { mutableStateOf(false) }
val rotation by animateFloatAsState(
if (flipped) 180f else 0f,
tween(600),
label = "flip"
)
Box(
modifier = Modifier
.fillMaxWidth()
.height(140.dp)
.padding(vertical = 8.dp)
.graphicsLayer {
rotationX = rotation
cameraDistance = 12f * density
}
.clickable { flipped = !flipped },
contentAlignment = Alignment.Center
) {
if (rotation <= 90f) {
// Front face
Surface(
modifier = Modifier.fillMaxSize(),
shape = RoundedCornerShape(20.dp),
color = color,
shadowElevation = 6.dp
) {
Box(contentAlignment = Alignment.Center) {
Text(front, color = Color.White,
fontSize = 26.sp,
fontWeight = FontWeight.Bold)
}
}
} else {
// Back face (counter-rotated)
Surface(
modifier = Modifier
.fillMaxSize()
.graphicsLayer { rotationX = 180f },
shape = RoundedCornerShape(20.dp),
color = color.copy(alpha = 0.8f),
shadowElevation = 6.dp
) {
Box(contentAlignment = Alignment.Center) {
Text(back, color = Color.White,
fontSize = 18.sp)
}
}
}
}
}
}
}
Tips and Pitfalls
- cameraDistance = 12f * density prevents the card from looking warped during rotation. Lower values create a fisheye effect.
- The 90-degree threshold determines when to swap front and back content. At exactly 90 degrees the card is edge-on and invisible.
- Counter-rotate the back face with
graphicsLayer { rotationX = 180f }so text is not mirrored. - Use tween(600) for a smooth flip. Faster than 400ms feels abrupt; slower than 800ms feels sluggish.