A Jetpack Compose card stack you can swipe through, expand and pick from, with physical, interruptible motion.
- Swipe the front card away to cycle forward, pull the back card in to cycle backward
- The dragged card lifts, tilts and follows the finger in every allowed direction
- Spring-based fly-out and landing that a new drag can interrupt, so rapid flicks are never dropped
- Tap to expand the stack along its alignment, tap a card to bring it to the front
- 8 stack alignments and 3 drag axis modes
- Optional hoisted state to read the front card and expand or collapse from code, kept across configuration change
- No recomposition per drag frame
dependencies {
implementation("io.github.teddko:cardstack:1.1.0")
}Requirements: minSdk 23, compileSdk 36 or higher, Kotlin 2.2 or higher, Compose 1.12.1 or higher.
@Composable
fun CardStackDemo() {
val items = remember { listOf("Card 1", "Card 2", "Card 3") }
DraggableCardStack(
initialItems = items,
height = 200.dp,
cardSpacingRatio = 0.1f,
cardAlignment = CardAlignment.BOTTOM,
dragAlignment = DragAlignment.NONE
) { item ->
Card(modifier = Modifier.fillMaxSize()) {
Text(text = item, modifier = Modifier.padding(16.dp))
}
}
}| Parameter | Description |
|---|---|
initialItems |
Cards from front to back. Changes to the list are rendered, new items join the back |
height |
Height of every card |
cardSpacingRatio |
Spacing of the collapsed stack, relative to the screen height |
cardAlignment |
Side the back cards peek out on, see Card alignment |
dragAlignment |
Axes a drag may move along, see Drag alignment |
modifier |
Modifier of the stack |
state |
Order and expansion state, see State |
cardContent |
Content of a card |
| Gesture | Result |
|---|---|
| Swipe right or up | The front card flies out and tucks behind the stack, the next card comes to the front |
| Swipe left or down | The back card slides out from behind the stack the other way (right for left, up for down), then lands on top in the swipe direction |
| Short drag | The card springs back, the order stays |
| Tap a collapsed stack | The stack expands |
| Tap a back card of an expanded stack | The card lifts, comes to the front and the stack collapses. The order rotates as many steps as the card's depth |
| Tap the front card of an expanded stack | The stack collapses |
- The first move of a drag picks the card: right or up drags the front card, left or down (past a small dead zone) pulls the back card. The picked card then follows the finger in every direction until release, and the other cards stay still.
- A release commits by distance (40% of the card size on the drag axis) or by velocity.
- A drag or a tap during an animation takes effect at once.
- A stack of fewer than two cards neither drags nor reacts to taps.
- TalkBack users can cycle the cards with the "Next card" and "Previous card" actions.
val state = rememberCardStackState()
DraggableCardStack(
initialItems = items,
height = 200.dp,
cardSpacingRatio = 0.1f,
cardAlignment = CardAlignment.BOTTOM,
dragAlignment = DragAlignment.NONE,
state = state
) { item -> Text(item) }
state.frontIndex // index in items of the front card, -1 for an empty stack
state.isExpanded
state.expand()
state.collapse()The order is kept as a permutation of item indices, so duplicate items are safe, and the order and expansion survive configuration change. Removing an item that is not the last one shifts the order.
Collapsed and expanded, from the demo app.
| Alignment | Collapsed / expanded |
|---|---|
BOTTOM |
![]() |
BOTTOM_START |
![]() |
BOTTOM_END |
![]() |
TOP |
![]() |
TOP_START |
![]() |
TOP_END |
![]() |
START |
![]() |
END |
![]() |
The vertical alignments widen the spacing and grow the stack height when expanded. START and END expand into a horizontal fan scaled to fit the stack width.
DragAlignment.VERTICAL // vertical moves only
DragAlignment.HORIZONTAL // horizontal moves only
DragAlignment.NONE // every direction- Existing
DraggableCardStackcalls keep working, binary and source compatible.modifierandstateare new optional parameters. - New behaviors: taps are consumed by the cards (expand, select, collapse), and expanding grows the stack height.
- Minimum Kotlin 2.2 and Compose 1.12.1.
- material3, activity-compose, appcompat and core-ktx no longer come in transitively. Declare them if your code uses them. Compose ui and foundation remain compile dependencies.
Apache License 2.0. See LICENSE.








