Estoy tratando de lograr esta forma personalizada con Compose
Pero por alguna razón, el círculo compensado del separador se dibuja con una línea de puntos y aquí está el código
@Preview @Composable private fun ReceiptSeparator () { Row(modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp) , verticalAlignment = Alignment.CenterVertically ,) { Box( modifier = Modifier .requiredSize(50.dp) .background(Color.White) .offset(-40.dp) .clip(CircleShape) .border(BorderStroke(2.dp, Color.Gray)) ){} Box( Modifier .height(1.dp) .requiredWidth(250.dp) .weight(3f) .background(Color.Gray, shape = DottedShape(step = 20.dp)) ){} Box( modifier = Modifier .offset(40.dp) .clip(CircleShape) .border(BorderStroke(2.dp, Color.Gray)) .background(Color.White) .size(50.dp) ){} } }¿Por qué el círculo se dibuja con una línea de puntos y cómo lograr esta forma correctamente?
Su círculo no se dibuja correctamente, porque Modifier.border dibuja un borde de rectángulo de manera predeterminada y luego lo recorta con su Modifier.clip . En cambio, si necesita aplicar una forma al borde, debe pasar la forma a Modifier.border , así:
.border(BorderStroke(2.dp, Color.Gray), shape = CircleShape)Pero esto no resolverá su problema. Para dibujar la sombra correctamente como se muestra en su imagen, debe aplicar una forma personalizada a su contenedor.
Puede usar Modifier.onGloballyPositioned para obtener la posición de sus límites:
var separatorOffsetY by remember { mutableStateOf<Float?>(null) } val cornerRadius = 20.dp Card( shape = RoundedCutoutShape(separatorOffsetY, cornerRadius), backgroundColor = Color.White, modifier = Modifier.padding(10.dp) ) { Column { Box(modifier = Modifier.height(200.dp)) Box( Modifier .padding(horizontal = cornerRadius) .height(1.dp) .requiredWidth(250.dp) // DottedShape is taken from this answer: // https://stackoverflow.com/a/68789205/3585796 .background(Color.Gray, shape = DottedShape(step = 20.dp)) .onGloballyPositioned { separatorOffsetY = it.boundsInParent().center.y } ) Box(modifier = Modifier.height(50.dp)) } }Con esta información, puede crear una forma como la siguiente:
class RoundedCutoutShape( private val offsetY: Float?, private val cornerRadiusDp: Dp, ) : Shape { override fun createOutline( size: Size, layoutDirection: LayoutDirection, density: Density, ) = Outline.Generic(run path@{ val cornerRadius = with(density) { cornerRadiusDp.toPx() } val rect = Rect(Offset.Zero, size) val mainPath = Path().apply { addRoundRect(RoundRect(rect, CornerRadius(cornerRadius))) } if (offsetY == null) return@path mainPath val cutoutPath = Path().apply { val circleSize = Size(cornerRadius, cornerRadius) * 2f val visiblePart = 0.25f val leftOval = Rect( offset = Offset( x = 0 - circleSize.width * (1 - visiblePart), y = offsetY - circleSize.height / 2 ), size = circleSize ) val rightOval = Rect( offset = Offset( x = rect.width - circleSize.width * visiblePart, y = offsetY - circleSize.height / 2 ), size = circleSize ) addOval(leftOval) addOval(rightOval) } return@path Path().apply { op(mainPath, cutoutPath, PathOperation.Difference) } }) }Resultado:

Deshacerse de:
shape = DottedShape(step = 20.dp)