androidkotlinandroid-jetpack-compose

How to make rounded Corner with shadow in jetpack compose


I want to make rounded corner with shadow for my card view. I am trying to make look like this

enter image description here

I am trying in Box with the help of Modifier

Using shadow and clip with ordering like this

Box(
    modifier = Modifier
        .fillMaxSize()
        .shadow(1.dp)
        .clip(RoundedCornerShape(12.dp))
        .padding(
            start = 16.dp,
            end = 16.dp,
            top = 12.dp,
            bottom = 16.dp,
        )
) {
    // method in here
}

and it looks like this

enter image description here

Now I changed to order like this clip and shadow

Box(
    modifier = Modifier
        .fillMaxSize()
        .clip(RoundedCornerShape(12.dp))
        .shadow(1.dp)
        .padding(
            start = 16.dp,
            end = 16.dp,
            top = 12.dp,
            bottom = 16.dp,
        )
) {
    // method in here
}

and it look like this

enter image description here

I don't understand ordering of modifier now. Can anyone guide me on this ?


Solution

  • TL;DR Use only Modifier.shadow(elevation, shape) which also clips based on shadow elevation.

    @Stable
    fun Modifier.shadow(
        elevation: Dp,
        shape: Shape = RectangleShape,
        clip: Boolean = elevation > 0.dp,
        ambientColor: Color = DefaultShadowColor,
        spotColor: Color = DefaultShadowColor,
    )
    

    Before understanding order of Modifiers i think it's better to explain what Modifier.shadow does and how it works.

    Modifier.shadow is basically a transparent background(see first sample where no background is assigned) and border around your Composable with some blur effect. If you draw it without a background you will have rectangle with blur based on elevation and spot color and order of it depends if it will be drawn behind or in front of your Composable.

    enter image description here

    @Preview
    @Composable
    private fun ShadowSample() {
    
        Column(
            modifier = Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
    
            Text("Shadow Order")
    
            Box(
                modifier = Modifier
                    .size(100.dp)
                    .shadow(
                        elevation = 10.dp,
                    spotColor = Color.Red,
                    shape = RoundedCornerShape(8.dp)
                    ),
                contentAlignment = Alignment.Center
            ) {
                Text("Hello World")
            }
    
            Spacer(modifier = Modifier.height(20.dp))
            Box(
                modifier = Modifier
                    .background(Color.Red)
                    .size(100.dp)
                    .shadow(
                        elevation = 10.dp,
                        shape = RoundedCornerShape(8.dp)
                    ),
                contentAlignment = Alignment.Center
            ) {
                Text("Hello World")
            }
    
            Spacer(modifier = Modifier.height(20.dp))
    
            Box(
                modifier = Modifier
                    .shadow(
                        elevation = 10.dp,
                        shape = RoundedCornerShape(8.dp)
                    )
                    .background(Color.Red)
                    .size(100.dp),
                contentAlignment = Alignment.Center
            ) {
                Text("Hello World")
            }
        }
    }
    

    First one is how it is without any background. In second one shadow is drawn after background so it's drawn in front of your Composable like drawing a border or Rect in front of it.

    Modifier.clip clips everything out of your Composable your border including any drawing(without clip you can draw anything out of your Composable) or shadow in our example as in example below

    enter image description here

    In first example we clip shadow and border that's why you don't see any shadow and green Rectangle border is clipped with rounded rectangle shape.

    In second example shadow is before clip so it's not clipped but green border is.

    In third example since border is before Modifier.shadow it's not clipped, as you can see it has rectangle shape. But anything after Modifier.shadow is clipped because Modifier.shadow comes with clip param true.

    @Preview
    @Composable
    private fun ShadowSample2(){
        Column(
            modifier = Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
    
            Text("Clipping with Shadow")
            Box(
                modifier = Modifier
                    .clip(RoundedCornerShape(8.dp))
                    .shadow(
                        elevation = 10.dp,
                        RoundedCornerShape(8.dp)
                    )
                    .border(1.dp, Color.Green)
                    .background(Color.White)
                    .size(100.dp),
                contentAlignment = Alignment.Center
            ) {
                Text("Hello World")
            }
    
            Spacer(modifier = Modifier.height(10.dp))
    
            Box(
                modifier = Modifier
    
                    .shadow(
                        elevation = 10.dp,
                        shape = RoundedCornerShape(8.dp)
                    )
                    .clip(RoundedCornerShape(8.dp))
                    .border(1.dp, Color.Green)
                    .background(Color.White)
                    .size(100.dp),
                contentAlignment = Alignment.Center
            ) {
                Text("Hello World")
            }
    
            Spacer(modifier = Modifier.height(10.dp))
    
            Box(
                modifier = Modifier
                    .border(1.dp, Color.Green)
                    .shadow(
                        elevation = 10.dp,
                        shape = RoundedCornerShape(8.dp)
                    )
                    .background(Color.White)
                    .size(100.dp),
                contentAlignment = Alignment.Center
            ) {
                Text("Hello World")
            }
        }
    }