Skip to content

Instantly share code, notes, and snippets.

@bmc08gt
Last active December 15, 2023 03:40
Show Gist options
  • Star 30 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bmc08gt/fca95db3bf9fcf255d76f03ec10ea3f9 to your computer and use it in GitHub Desktop.
Save bmc08gt/fca95db3bf9fcf255d76f03ec10ea3f9 to your computer and use it in GitHub Desktop.
import androidx.compose.animation.*
import androidx.compose.animation.core.tween
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.onCommit
import androidx.compose.ui.Modifier
@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterialApi::class)
@Composable
fun <T> AnimatedSwipeDismiss(
modifier: Modifier = Modifier,
item: T,
background: @Composable (isDismissed: Boolean) -> Unit,
content: @Composable (isDismissed: Boolean) -> Unit,
directions: Set<DismissDirection> = setOf(DismissDirection.EndToStart),
enter: EnterTransition = expandVertically(),
exit: ExitTransition = shrinkVertically(
animSpec = tween(
durationMillis = 500,
)
),
onDismiss: (T) -> Unit
) {
val dismissState = rememberDismissState()
val isDismissed = dismissState.isDismissed(DismissDirection.EndToStart)
onCommit(dismissState.value) {
if (dismissState.value == DismissValue.DismissedToStart) {
onDismiss(item)
}
}
AnimatedVisibility(
modifier = modifier,
visible = !isDismissed,
enter = enter,
exit = exit
) {
SwipeToDismiss(
modifier = modifier,
state = dismissState,
directions = directions,
background = { background(isDismissed) },
dismissContent = { content(isDismissed) }
)
}
}
@Composable
fun <T> ListContent(
innerPadding: InnerPadding,
items: List<T>,
onSwipe: (T) -> Unit,
onClick: (T) -> Unit
) {
LazyColumnFor(
modifier = modifier.padding(innerPadding),
items = items,
) { item ->
AnimatedSwipeDismiss(
item = item,
background = { isDismissed ->
/** define your background delete view here
* possibly:
Box(
modifier = Modifier.fillMaxSize(),
backgroundColor = Color.Red,
paddingStart = 20.dp,
paddingEnd = 20.dp,
gravity = ContentGravity.CenterEnd
) {
val alpha = animate( if (isDismissed) 0f else 1f)
Icon(Icons.Filled.Delete, tint = Color.White.copy(alpha = alpha))
}
using isDismissed to control alpha of the icon or content in the box
*/
},
content = { /* your item cell (feed your on click here) */ },
onDismiss = { onSwipe(it) }
}
}
@LouisCAD
Copy link

Putting the link of the Tweet with video for reference: https://twitter.com/brandonmcansh/status/1305538643604525058?s=19

@bmc08gt
Copy link
Author

bmc08gt commented Sep 15, 2020

Thanks Louis!

@msfjarvis
Copy link

msfjarvis commented Sep 18, 2020

function ListContent( in ListContent.kt should be fun <T> ListContent( for this to compile.

Edit: also missing a closing parentheses for the AnimatedSwipeDismiss invocation in the same file. Final version that compiles:

@Composable
fun <T> ListContent(
  innerPadding: PaddingValues,
  items: List<T>,
  onSwipe: (T) -> Unit,
  onClick: (T) -> Unit,
  modifier: Modifier = Modifier,
) {
  LazyColumnFor(
    modifier = modifier.padding(innerPadding),
    items = items,
  ) { item ->
    AnimatedSwipeDismiss(
      item = item,
      background = { isDismissed ->
        /** define your background delete view here
         * possibly:
        Box(
        modifier = Modifier.fillMaxSize(),
        backgroundColor = Color.Red,
        paddingStart = 20.dp,
        paddingEnd = 20.dp,
        gravity = ContentGravity.CenterEnd
        ) {
        val alpha = animate( if (isDismissed) 0f else 1f)
        Icon(Icons.Filled.Delete, tint = Color.White.copy(alpha = alpha))
        }

        using isDismissed to control alpha of the icon or content in the box
         */
      },
      content = { /* your item cell (feed your on click here) */ },
      onDismiss = { onSwipe(it) }
    )
  }
}

@bmc08gt
Copy link
Author

bmc08gt commented Sep 19, 2020

function ListContent( in ListContent.kt should be fun <T> ListContent( for this to compile.

Edit: also missing a closing parentheses for the AnimatedSwipeDismiss invocation in the same file. Final version that compiles:

@Composable
fun <T> ListContent(
  innerPadding: PaddingValues,
  items: List<T>,
  onSwipe: (T) -> Unit,
  onClick: (T) -> Unit,
  modifier: Modifier = Modifier,
) {
  LazyColumnFor(
    modifier = modifier.padding(innerPadding),
    items = items,
  ) { item ->
    AnimatedSwipeDismiss(
      item = item,
      background = { isDismissed ->
        /** define your background delete view here
         * possibly:
        Box(
        modifier = Modifier.fillMaxSize(),
        backgroundColor = Color.Red,
        paddingStart = 20.dp,
        paddingEnd = 20.dp,
        gravity = ContentGravity.CenterEnd
        ) {
        val alpha = animate( if (isDismissed) 0f else 1f)
        Icon(Icons.Filled.Delete, tint = Color.White.copy(alpha = alpha))
        }

        using isDismissed to control alpha of the icon or content in the box
         */
      },
      content = { /* your item cell (feed your on click here) */ },
      onDismiss = { onSwipe(it) }
    )
  }
}

Thanks! Copy/paste fail - updated

@imallan
Copy link

imallan commented Oct 8, 2020

I've found val dismissState = rememberDismissState() remembering the dismissed state after the item was deleted from the List items. I've changed it to val dissmissState = remember { DismissState(...) } as a workaround.

@rex50
Copy link

rex50 commented Dec 16, 2021

Hey @bmc08gt,

Thanks for this code snippet but androidx.compose.runtime.onCommit is deprecated and removed in the latest compose stable versions (tested on compose version 1.0.5), so I've updated the snippet for AnimatedSwipeDismiss.kt, please consider updating your version.

import androidx.compose.animation.core.tween
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.Modifier

@ExperimentalMaterialApi
@ExperimentalAnimationApi
@Composable
fun <T> AnimatedSwipeDismiss(
    modifier: Modifier = Modifier,
    item: T,
    background: @Composable (isDismissed: Boolean) -> Unit,
    content: @Composable (isDismissed: Boolean) -> Unit,
    directions: Set<DismissDirection> = setOf(DismissDirection.EndToStart),
    enter: EnterTransition = expandVertically(),
    exit: ExitTransition = shrinkVertically(
        animationSpec = tween(
            durationMillis = 500,
        )
    ),
    onDismiss: (T) -> Unit
) {
    val dismissState = rememberDismissState()
    val isDismissed = dismissState.isDismissed(DismissDirection.EndToStart)

    SideEffect {
        if (dismissState.isDismissed(DismissDirection.EndToStart)) {
            onDismiss(item)
        }
    }

    AnimatedVisibility(
        modifier = modifier,
        visible = !isDismissed,
        enter = enter,
        exit = exit
    ) {
        SwipeToDismiss(
            modifier = modifier,
            state = dismissState,
            directions = directions,
            background = { background(isDismissed) },
            dismissContent = { content(isDismissed) }
        )
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment