Created
March 7, 2024 21:13
-
-
Save burntcookie90/be719394fa38df8a0f0741b882f13a96 to your computer and use it in GitHub Desktop.
Revisions
-
burntcookie90 created this gist
Mar 7, 2024 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,181 @@ import androidx.annotation.DrawableRes import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Icon import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.material.rememberSwipeableState import androidx.compose.material.swipeable import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import kotlin.math.roundToInt @OptIn(ExperimentalMaterialApi::class) @Composable fun <T> SwipeableItemRow( modifier: Modifier, item: T, swiped: Boolean, onSwipe: (T, Boolean) -> Unit, onClick: () -> Unit, actionButtons: List<ActionButtonModel>, itemRow: @Composable (Modifier) -> Unit, ) { val swipeableState = rememberSwipeableState( initialValue = false, confirmStateChange = { onSwipe(item, it) it }, ) LaunchedEffect(swiped) { swipeableState.animateTo(swiped) } BoxWithConstraints( modifier = modifier .wrapContentHeight(), ) { val (revealedRatio, swipedRatio) = actionButtons.size.let { require(it <= 3) { "Only 3 action buttons are supported" } when (it) { 1 -> 0.70f to 0.3f 2 -> 0.50f to 0.5f 3 -> 0.30f to 0.7f else -> error("Unhandled") } } val swipeWidth = constraints.maxWidth * swipedRatio val boxScope = this Box( modifier = Modifier .wrapContentHeight() .swipeable( state = swipeableState, anchors = mapOf(-swipeWidth to true, 0f to false), orientation = Orientation.Horizontal, ), ) { Row( modifier = Modifier .matchParentSize() .align(Alignment.CenterEnd) .padding(start = boxScope.maxWidth * revealedRatio), verticalAlignment = Alignment.CenterVertically, ) { actionButtons.forEach { ActionButton( modifier = Modifier .fillMaxHeight() .weight(1f), model = it ) } } val baseCornerRadius = 8.dp val fractionalCornerRadius = baseCornerRadius * swipeableState.progress.fraction val cornerCalculation = when { swipeableState.progress.to -> fractionalCornerRadius swipeableState.progress.from -> baseCornerRadius - fractionalCornerRadius else -> 0.dp } val baseElevation = 8.dp val fractionalElevation = baseElevation * swipeableState.progress.fraction val elevationCalculation = when { swipeableState.progress.to -> fractionalElevation swipeableState.progress.from -> baseElevation - fractionalElevation else -> 0.dp } val cornerRadius = animateDpAsState(targetValue = cornerCalculation, label = "rowCorner") val elevation = animateDpAsState(targetValue = elevationCalculation, label = "rowElevation") val shape = RoundedCornerShape(topEnd = cornerRadius.value, bottomEnd = cornerRadius.value) Column { itemRow( Modifier .offset { IntOffset( x = swipeableState.offset.value.roundToInt(), y = 0, ) } .clickable { onClick() } .clip(shape) .shadow( elevation = elevation.value, shape = shape, ), ) } } } } data class ActionButtonModel( val backgroundColor: Color, val onClick: () -> Unit, @DrawableRes val iconResId: Int, val iconTint: Color = Color.Unspecified, val text: String, ) @Composable fun ActionButton( modifier: Modifier = Modifier, model: ActionButtonModel ) { Button( modifier = modifier, colors = ButtonDefaults.buttonColors(backgroundColor = model.backgroundColor), shape = RectangleShape, onClick = { model.onClick() }, ) { Column( modifier = Modifier.fillMaxHeight(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) { Icon( modifier = Modifier.size(16.dp), painter = painterResource(id = model.iconResId), contentDescription = null, tint = model.iconTint, ) Spacer(modifier = Modifier.size(8.dp)) Text( text = model.text, style = MaterialTheme.typography.subtitle2.copy(fontWeight = FontWeight.Bold), ) } } }