https://kotlinlang.org logo
#compose
Title
# compose
c

Colton Idle

02/18/2021, 4:05 AM
Trying to implement this in compose, but I'm running out of ideas. This banner will say things like "Free" and "Paid" but my designer wants to have this little "tail"/"wraparound" going on for the banner. Offsetting a rectangle that holds text isn't hard, but the tail is killing me. Ideas?
h

Halil Ozercan

02/18/2021, 5:21 AM
Think of the wrapped around part as a vertical rounded rectangle. Color it also with bright red but put also a half rounded rectangle at the top which is the small, dark red one. Wouldn't that work?
s

Samir Basnet

02/18/2021, 1:34 PM
Or may be ask designer for SVG file and use that as compose supports SVG
1
n

Nader Jawad

02/18/2021, 10:11 PM
I hacked up something quickly in compose that gets you something similar here. You probably would need to tweak the radii/sizing/colors but it's somewhat close:
Copy code
fun Modifier.banner(): Modifier {
    val bannerHeight = 50.dp
    val bannerWidth = 80.dp
    val tailWidth = 15.dp
    val tailHeight = 15.dp
    val tailPadding = 2.dp
    return this.then(Modifier.drawWithCache {
        val bannerPath = Path().apply {
            addRoundRect(
                RoundRect(
                    left = size.width + tailWidth.toPx() - bannerWidth.toPx(),
                    top = size.height / 2 - bannerHeight.toPx() / 2,
                    right = size.width + tailWidth.toPx(),
                    bottom = size.height / 2 + bannerHeight.toPx() / 2,
                    topLeftCornerRadius = CornerRadius(15f),
                    bottomLeftCornerRadius = CornerRadius(15f),
                    bottomRightCornerRadius = CornerRadius(15f)
                )
            )
        }
        val bannerTailOuter = Path().apply {
            addRoundRect(
                RoundRect(
                    left = size.width,
                    top = size.height / 2 - bannerHeight.toPx() / 2 - tailHeight.toPx(),
                    right = size.width + tailWidth.toPx(),
                    bottom = size.height / 2 - bannerHeight.toPx() / 2,
                    topRightCornerRadius = CornerRadius(15f)
                )
            )
        }
        val bannerTailInner = Path().apply {
            addRoundRect(
                RoundRect(
                    left = size.width,
                    top = size.height / 2 - bannerHeight.toPx() / 2 - tailHeight.toPx() +
                        tailPadding.toPx(),
                    right = size.width + tailWidth.toPx() - tailPadding.toPx(),
                    bottom = size.height / 2 - bannerHeight.toPx() / 2,
                    topRightCornerRadius = CornerRadius(15f),
                    bottomRightCornerRadius = CornerRadius(15f)
                )
            )
        }
        onDrawWithContent {
            drawPath(bannerTailOuter, Color.Gray)
            drawPath(bannerTailInner, Color.DarkGray)
            drawContent()
            drawPath(bannerPath, Color.Gray)
        }
    })
}

@Composable
fun BannerDemo() {
    Box(modifier = Modifier.fillMaxSize()) {
        Box(modifier = Modifier.size(200.dp).background(Color.Blue).banner())
    }
}
The nice thing about this is that it is a Modifier that can be added to any composable. This sample code generates the following output where the banner is centered on whatever composable the modifier is attached to.
c

Colton Idle

02/18/2021, 11:38 PM
@Nader Jawad wow. Thank you for your time and thoughtfulness. This will really help me and others. Thank you!
👍 1
n

Nader Jawad

02/18/2021, 11:40 PM
No problem! Hopefully it helps. It was kind of fun to build as well :-)
🎉 1
2 Views