Skip to content

Commit 03e2b27

Browse files
Refactor swipe-to-dismiss examples (#486)
* Refactor swipe-to-dismiss examples - Update TodoItem data class for better readability - Refactor the swipe item to improve the overall performance and readability - Refactor SwipeItemExample for better readability - Refactor SwipeCardItemExample and rename it to SwipeItemWithAnimationExample for better description - Modify the swipe to dismiss animation for better user experience - Add comments to make code easy to read * Apply Spotless --------- Co-authored-by: JolandaVerhoef <[email protected]>
1 parent 43bba58 commit 03e2b27

File tree

1 file changed

+104
-176
lines changed

1 file changed

+104
-176
lines changed

compose/snippets/src/main/java/com/example/compose/snippets/components/SwipeToDismissBox.kt

+104-176
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,12 @@
1616

1717
package com.example.compose.snippets.components
1818

19-
import androidx.compose.animation.animateColorAsState
2019
import androidx.compose.foundation.background
2120
import androidx.compose.foundation.layout.Arrangement
2221
import androidx.compose.foundation.layout.Column
23-
import androidx.compose.foundation.layout.Row
24-
import androidx.compose.foundation.layout.Spacer
2522
import androidx.compose.foundation.layout.fillMaxSize
2623
import androidx.compose.foundation.layout.padding
24+
import androidx.compose.foundation.layout.wrapContentSize
2725
import androidx.compose.foundation.lazy.LazyColumn
2826
import androidx.compose.foundation.lazy.items
2927
import androidx.compose.material.icons.Icons
@@ -34,15 +32,17 @@ import androidx.compose.material3.Icon
3432
import androidx.compose.material3.ListItem
3533
import androidx.compose.material3.OutlinedCard
3634
import androidx.compose.material3.SwipeToDismissBox
37-
import androidx.compose.material3.SwipeToDismissBoxValue
35+
import androidx.compose.material3.SwipeToDismissBoxValue.EndToStart
36+
import androidx.compose.material3.SwipeToDismissBoxValue.Settled
37+
import androidx.compose.material3.SwipeToDismissBoxValue.StartToEnd
3838
import androidx.compose.material3.Text
3939
import androidx.compose.material3.rememberSwipeToDismissBoxState
4040
import androidx.compose.runtime.Composable
41-
import androidx.compose.runtime.getValue
4241
import androidx.compose.runtime.mutableStateListOf
4342
import androidx.compose.runtime.remember
4443
import androidx.compose.ui.Alignment
4544
import androidx.compose.ui.Modifier
45+
import androidx.compose.ui.draw.drawBehind
4646
import androidx.compose.ui.graphics.Color
4747
import androidx.compose.ui.graphics.RectangleShape
4848
import androidx.compose.ui.graphics.lerp
@@ -63,102 +63,71 @@ fun SwipeToDismissBoxExamples() {
6363
Text("Swipe to dismiss with change of background", fontWeight = FontWeight.Bold)
6464
SwipeItemExample()
6565
Text("Swipe to dismiss with a cross-fade animation", fontWeight = FontWeight.Bold)
66-
SwipeCardItemExample()
66+
SwipeItemWithAnimationExample()
6767
}
6868
}
6969

7070
// [START android_compose_components_todoitem]
7171
data class TodoItem(
72-
var isItemDone: Boolean,
73-
var itemDescription: String
72+
val itemDescription: String,
73+
var isItemDone: Boolean = false
7474
)
7575
// [END android_compose_components_todoitem]
7676

7777
// [START android_compose_components_swipeitem]
7878
@Composable
79-
fun SwipeItem(
79+
fun TodoListItem(
8080
todoItem: TodoItem,
81-
startToEndAction: (TodoItem) -> Unit,
82-
endToStartAction: (TodoItem) -> Unit,
81+
onToggleDone: (TodoItem) -> Unit,
82+
onRemove: (TodoItem) -> Unit,
8383
modifier: Modifier = Modifier,
84-
content: @Composable (TodoItem) -> Unit
8584
) {
8685
val swipeToDismissBoxState = rememberSwipeToDismissBoxState(
8786
confirmValueChange = {
88-
when (it) {
89-
SwipeToDismissBoxValue.StartToEnd -> {
90-
startToEndAction(todoItem)
91-
// Do not dismiss this item.
92-
false
93-
}
94-
SwipeToDismissBoxValue.EndToStart -> {
95-
endToStartAction(todoItem)
96-
true
97-
}
98-
SwipeToDismissBoxValue.Settled -> {
99-
false
100-
}
101-
}
87+
if (it == StartToEnd) onToggleDone(todoItem)
88+
else if (it == EndToStart) onRemove(todoItem)
89+
// Reset item when toggling done status
90+
it != StartToEnd
10291
}
10392
)
10493

10594
SwipeToDismissBox(
10695
state = swipeToDismissBoxState,
10796
modifier = modifier.fillMaxSize(),
10897
backgroundContent = {
109-
Row(
110-
modifier = Modifier
111-
.background(
112-
when (swipeToDismissBoxState.dismissDirection) {
113-
SwipeToDismissBoxValue.StartToEnd -> {
114-
Color.Blue
115-
}
116-
SwipeToDismissBoxValue.EndToStart -> {
117-
Color.Red
118-
}
119-
SwipeToDismissBoxValue.Settled -> {
120-
Color.LightGray
121-
}
122-
}
98+
when (swipeToDismissBoxState.dismissDirection) {
99+
StartToEnd -> {
100+
Icon(
101+
if (todoItem.isItemDone) Icons.Default.CheckBox else Icons.Default.CheckBoxOutlineBlank,
102+
contentDescription = if (todoItem.isItemDone) "Done" else "Not done",
103+
modifier = Modifier
104+
.fillMaxSize()
105+
.background(Color.Blue)
106+
.wrapContentSize(Alignment.CenterStart)
107+
.padding(12.dp),
108+
tint = Color.White
123109
)
124-
.fillMaxSize(),
125-
verticalAlignment = Alignment.CenterVertically,
126-
horizontalArrangement = Arrangement.SpaceBetween
127-
) {
128-
when (swipeToDismissBoxState.dismissDirection) {
129-
SwipeToDismissBoxValue.StartToEnd -> {
130-
val icon = if (todoItem.isItemDone) {
131-
Icons.Default.CheckBox
132-
} else {
133-
Icons.Default.CheckBoxOutlineBlank
134-
}
135-
136-
val contentDescription = if (todoItem.isItemDone) "Done" else "Not done"
137-
138-
Icon(
139-
icon,
140-
contentDescription,
141-
Modifier.padding(12.dp),
142-
tint = Color.White
143-
)
144-
}
145-
146-
SwipeToDismissBoxValue.EndToStart -> {
147-
Spacer(modifier = Modifier)
148-
Icon(
149-
imageVector = Icons.Default.Delete,
150-
contentDescription = "Remove item",
151-
tint = Color.White,
152-
modifier = Modifier.padding(12.dp)
153-
)
154-
}
155-
156-
SwipeToDismissBoxValue.Settled -> {}
157110
}
111+
EndToStart -> {
112+
Icon(
113+
imageVector = Icons.Default.Delete,
114+
contentDescription = "Remove item",
115+
modifier = Modifier
116+
.fillMaxSize()
117+
.background(Color.Red)
118+
.wrapContentSize(Alignment.CenterEnd)
119+
.padding(12.dp),
120+
tint = Color.White
121+
)
122+
}
123+
Settled -> {}
158124
}
159125
}
160126
) {
161-
content(todoItem)
127+
ListItem(
128+
headlineContent = { Text(todoItem.itemDescription) },
129+
supportingContent = { Text("swipe me to update or remove.") }
130+
)
162131
}
163132
}
164133
// [END android_compose_components_swipeitem]
@@ -169,10 +138,8 @@ fun SwipeItem(
169138
private fun SwipeItemExample() {
170139
val todoItems = remember {
171140
mutableStateListOf(
172-
TodoItem(isItemDone = false, itemDescription = "Pay bills"),
173-
TodoItem(isItemDone = false, itemDescription = "Buy groceries"),
174-
TodoItem(isItemDone = false, itemDescription = "Go to gym"),
175-
TodoItem(isItemDone = false, itemDescription = "Get dinner")
141+
TodoItem("Pay bills"), TodoItem("Buy groceries"),
142+
TodoItem("Go to gym"), TodoItem("Get dinner")
176143
)
177144
}
178145

@@ -181,124 +148,91 @@ private fun SwipeItemExample() {
181148
items = todoItems,
182149
key = { it.itemDescription }
183150
) { todoItem ->
184-
SwipeItem(
151+
TodoListItem(
185152
todoItem = todoItem,
186-
startToEndAction = {
153+
onToggleDone = { todoItem ->
187154
todoItem.isItemDone = !todoItem.isItemDone
188155
},
189-
endToStartAction = {
156+
onRemove = { todoItem ->
190157
todoItems -= todoItem
191-
}
192-
) {
193-
ListItem(
194-
headlineContent = { Text(text = todoItem.itemDescription) },
195-
supportingContent = { Text(text = "swipe me to update or remove.") }
196-
)
197-
}
158+
},
159+
modifier = Modifier.animateItem()
160+
)
198161
}
199162
}
200163
}
201164
// [END android_compose_components_swipeitemexample]
202165

203166
// [START android_compose_components_swipecarditem]
204167
@Composable
205-
fun SwipeCardItem(
168+
fun TodoListItemWithAnimation(
206169
todoItem: TodoItem,
207-
startToEndAction: (TodoItem) -> Unit,
208-
endToStartAction: (TodoItem) -> Unit,
170+
onToggleDone: (TodoItem) -> Unit,
171+
onRemove: (TodoItem) -> Unit,
209172
modifier: Modifier = Modifier,
210-
content: @Composable (TodoItem) -> Unit
211173
) {
212-
val swipeToDismissState = rememberSwipeToDismissBoxState(
213-
positionalThreshold = { totalDistance -> totalDistance * 0.25f },
214-
// [START_EXCLUDE]
174+
val swipeToDismissBoxState = rememberSwipeToDismissBoxState(
215175
confirmValueChange = {
216-
when (it) {
217-
SwipeToDismissBoxValue.StartToEnd -> {
218-
startToEndAction(todoItem)
219-
// Do not dismiss this item.
220-
false
221-
}
222-
SwipeToDismissBoxValue.EndToStart -> {
223-
endToStartAction(todoItem)
224-
true
225-
}
226-
SwipeToDismissBoxValue.Settled -> {
227-
false
228-
}
229-
}
176+
if (it == StartToEnd) onToggleDone(todoItem)
177+
else if (it == EndToStart) onRemove(todoItem)
178+
// Reset item when toggling done status
179+
it != StartToEnd
230180
}
231181
)
232182

233-
// [END_EXCLUDE]
234183
SwipeToDismissBox(
235-
modifier = Modifier,
236-
state = swipeToDismissState,
184+
state = swipeToDismissBoxState,
185+
modifier = modifier.fillMaxSize(),
237186
backgroundContent = {
238-
// Cross-fade the background color as the drag gesture progresses.
239-
val color by animateColorAsState(
240-
when (swipeToDismissState.targetValue) {
241-
SwipeToDismissBoxValue.Settled -> Color.LightGray
242-
SwipeToDismissBoxValue.StartToEnd ->
243-
lerp(Color.LightGray, Color.Blue, swipeToDismissState.progress)
244-
245-
SwipeToDismissBoxValue.EndToStart ->
246-
lerp(Color.LightGray, Color.Red, swipeToDismissState.progress)
247-
},
248-
label = "swipeable card item background color"
249-
)
250-
// [START_EXCLUDE]
251-
Row(
252-
modifier = Modifier
253-
.background(color)
254-
.fillMaxSize(),
255-
verticalAlignment = Alignment.CenterVertically,
256-
horizontalArrangement = Arrangement.SpaceBetween
257-
) {
258-
when (swipeToDismissState.dismissDirection) {
259-
SwipeToDismissBoxValue.StartToEnd -> {
260-
val icon = if (todoItem.isItemDone) {
261-
Icons.Default.CheckBox
262-
} else {
263-
Icons.Default.CheckBoxOutlineBlank
264-
}
265-
266-
val contentDescription = if (todoItem.isItemDone) "Done" else "Not done"
267-
268-
Icon(icon, contentDescription, Modifier.padding(12.dp), tint = Color.White)
269-
}
270-
271-
SwipeToDismissBoxValue.EndToStart -> {
272-
Spacer(modifier = Modifier)
273-
Icon(
274-
imageVector = Icons.Default.Delete,
275-
contentDescription = "Remove item",
276-
tint = Color.White,
277-
modifier = Modifier.padding(12.dp)
278-
)
279-
}
280-
281-
SwipeToDismissBoxValue.Settled -> {}
187+
when (swipeToDismissBoxState.dismissDirection) {
188+
StartToEnd -> {
189+
Icon(
190+
if (todoItem.isItemDone) Icons.Default.CheckBox else Icons.Default.CheckBoxOutlineBlank,
191+
contentDescription = if (todoItem.isItemDone) "Done" else "Not done",
192+
modifier = Modifier
193+
.fillMaxSize()
194+
.drawBehind {
195+
drawRect(lerp(Color.LightGray, Color.Blue, swipeToDismissBoxState.progress))
196+
}
197+
.wrapContentSize(Alignment.CenterStart)
198+
.padding(12.dp),
199+
tint = Color.White
200+
)
282201
}
202+
EndToStart -> {
203+
Icon(
204+
imageVector = Icons.Default.Delete,
205+
contentDescription = "Remove item",
206+
modifier = Modifier
207+
.fillMaxSize()
208+
.background(lerp(Color.LightGray, Color.Red, swipeToDismissBoxState.progress))
209+
.wrapContentSize(Alignment.CenterEnd)
210+
.padding(12.dp),
211+
tint = Color.White
212+
)
213+
}
214+
Settled -> {}
283215
}
284216
}
285217
) {
286-
content(todoItem)
218+
OutlinedCard(shape = RectangleShape) {
219+
ListItem(
220+
headlineContent = { Text(todoItem.itemDescription) },
221+
supportingContent = { Text("swipe me to update or remove.") }
222+
)
223+
}
287224
}
288-
// [END_EXCLUDE]
289225
}
290226
// [END android_compose_components_swipecarditem]
291227

292-
// [START android_compose_components_swipecarditemexample]
293228
@Preview
229+
// [START android_compose_components_swipecarditemexample]
294230
@Composable
295-
private fun SwipeCardItemExample() {
231+
private fun SwipeItemWithAnimationExample() {
296232
val todoItems = remember {
297233
mutableStateListOf(
298-
TodoItem(isItemDone = false, itemDescription = "Pay bills"),
299-
TodoItem(isItemDone = false, itemDescription = "Buy groceries"),
300-
TodoItem(isItemDone = false, itemDescription = "Go to gym"),
301-
TodoItem(isItemDone = false, itemDescription = "Get dinner")
234+
TodoItem("Pay bills"), TodoItem("Buy groceries"),
235+
TodoItem("Go to gym"), TodoItem("Get dinner")
302236
)
303237
}
304238

@@ -307,22 +241,16 @@ private fun SwipeCardItemExample() {
307241
items = todoItems,
308242
key = { it.itemDescription }
309243
) { todoItem ->
310-
SwipeCardItem(
244+
TodoListItemWithAnimation(
311245
todoItem = todoItem,
312-
startToEndAction = {
246+
onToggleDone = { todoItem ->
313247
todoItem.isItemDone = !todoItem.isItemDone
314248
},
315-
endToStartAction = {
249+
onRemove = { todoItem ->
316250
todoItems -= todoItem
317-
}
318-
) {
319-
OutlinedCard(shape = RectangleShape) {
320-
ListItem(
321-
headlineContent = { Text(todoItem.itemDescription) },
322-
supportingContent = { Text("swipe me to update or remove.") }
323-
)
324-
}
325-
}
251+
},
252+
modifier = Modifier.animateItem()
253+
)
326254
}
327255
}
328256
}

0 commit comments

Comments
 (0)