Skip to content

Instantly share code, notes, and snippets.

@fvilarino
Created March 17, 2024 03:03
Show Gist options
  • Save fvilarino/7f8980c41c88653420f86d9b4dca2070 to your computer and use it in GitHub Desktop.
Save fvilarino/7f8980c41c88653420f86d9b4dca2070 to your computer and use it in GitHub Desktop.
Chip Selector - Animated Border
@Composable
fun Chip(
label: String,
isSelected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
val interactionSource = remember { MutableInteractionSource() }
val borderColor = MaterialTheme.colorScheme.onSurface
val backgroundColor = if (isSelected) {
MaterialTheme.colorScheme.primaryContainer
} else {
MaterialTheme.colorScheme.secondaryContainer
}
val textColor = if (isSelected) {
MaterialTheme.colorScheme.onPrimaryContainer
} else {
MaterialTheme.colorScheme.onSecondaryContainer
}
val path = remember { Path() }
val borderWidth = with(LocalDensity.current) { 2.dp.toPx() }
// 1
val pathMeasure = remember { PathMeasure() }
val pathSegment = remember { Path() }
// 2
val transition = updateTransition(targetState = isSelected, label = "transition")
// 3
val pathFraction by transition.animateFloat(
transitionSpec = { tween(durationMillis = AnimationDurationMillis) },
label = "pathSegment"
) { selected ->
if (selected) 1f else 0f
}
Box(
modifier = modifier
.drawWithCache {
val cornerRadius = size.height / 2f
path.moveTo(borderWidth + size.width / 2f, borderWidth)
path.lineTo(size.width - borderWidth - cornerRadius, borderWidth)
path.quadraticBezierTo(
size.width - borderWidth,
borderWidth,
size.width - borderWidth,
borderWidth + cornerRadius
)
path.quadraticBezierTo(
size.width - borderWidth,
size.height - borderWidth,
size.width - borderWidth - cornerRadius,
size.height - borderWidth
)
path.lineTo(borderWidth + cornerRadius, size.height - borderWidth)
path.quadraticBezierTo(
borderWidth,
size.height - borderWidth,
borderWidth,
size.height - cornerRadius - borderWidth
)
path.quadraticBezierTo(
borderWidth,
borderWidth,
borderWidth + cornerRadius,
borderWidth
)
path.close()
// 4
pathMeasure.setPath(path, false)
// 5
pathSegment.reset()
// 6
pathMeasure.getSegment(0f, pathMeasure.length * pathFraction, pathSegment)
onDrawBehind {
drawPath(
path = path,
color = backgroundColor,
style = Fill,
)
// 7
drawPath(
path = pathSegment,
color = borderColor,
style = Stroke(width = borderWidth),
)
}
}
.clickable(interactionSource = interactionSource, indication = null, onClick = onClick)
) {
Text(
text = label,
color = textColor,
fontWeight = FontWeight.Bold,
modifier = Modifier
.padding(horizontal = 24.dp, vertical = 16.dp)
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment