Documentation
animate:flip
Smooth animations when list items are reordered, added, or removed.
Overview
The animate:flip directive automatically animates position changes in lists.
When items move, they smoothly transition from their old position to their new position.
Basic Usage
Add animate:flip to items in a LazyColumn, LazyRow, or Column:
<LazyColumn>
@for (item in items) {
<Row key={item.id} animate:flip>
<Text>{item.name}</Text>
</Row>
}
</LazyColumn> Always provide a key attribute when using animate:flip.
This helps the framework track which item is which.
Configuration
Customize animation timing:
<Row key={item.id} animate:flip={{ duration: 200 }}>
<Text>{item.name}</Text>
</Row> Options
| Option | Type | Default | Description |
|---|---|---|---|
duration | Number | 300 | Animation duration in milliseconds |
easing | String | "ease" | Easing function |
Examples
Sortable List
var items = ["Apple", "Banana", "Cherry"]
fun shuffle() {
items = items.shuffled()
}
<Column>
<Button onClick={shuffle} text="Shuffle" />
<LazyColumn gap={8}>
@for (item in items) {
<Card key={item} animate:flip={{ duration: 300 }}>
<Text>{item}</Text>
</Card>
}
</LazyColumn>
</Column> Drag to Reorder
var todos = [...]
<LazyColumn>
@for (todo in todos) {
<Row
key={todo.id}
animate:flip={{ duration: 250 }}
draggable
onReorder={(from, to) => reorder(from, to)}
>
<Text>{todo.text}</Text>
</Row>
}
</LazyColumn> Add/Remove with Animation
var messages = []
fun addMessage(text: String) {
messages = messages + Message(id = UUID(), text = text)
}
fun removeMessage(id: String) {
messages = messages.filter { it.id != id }
}
<LazyColumn>
@for (msg in messages) {
<Card
key={msg.id}
animate:flip
in:slide={{ from: "bottom" }}
out:slide={{ to: "right" }}
>
<Text>{msg.text}</Text>
<Button onClick={() => removeMessage(msg.id)} text="Delete" />
</Card>
}
</LazyColumn> Combine animate:flip with in: and out: directives
for complete control over enter, exit, and reorder animations.
Filter/Sort Animation
var products = [...]
var filter = "all"
val filtered = when (filter) {
"featured" -> products.filter { it.featured }
"sale" -> products.filter { it.onSale }
else -> products
}
<Column>
<TabRow>
<Tab onClick={() => filter = "all"}>All</Tab>
<Tab onClick={() => filter = "featured"}>Featured</Tab>
<Tab onClick={() => filter = "sale"}>Sale</Tab>
</TabRow>
<LazyVerticalGrid columns={2} gap={12}>
@for (product in filtered) {
<Card
key={product.id}
animate:flip={{ duration: 200, easing: "ease-out" }}
>
<Image src={product.image} />
<Text>{product.name}</Text>
</Card>
}
</LazyVerticalGrid>
</Column> How It Works
Under the hood, animate:flip transpiles to Compose's Modifier.animateItem().
The framework tracks each item's position and smoothly animates changes using layout animations.
Performance
- Animations run on the composition thread for smoothness
- Only visible items are animated in lazy lists
- Items outside the viewport skip animation for better performance
See Also
- Transitions - Enter/exit animations
- Lazy Lists - LazyColumn, LazyRow, LazyVerticalGrid
- Progress-Driven Morphing - Gesture-based animations