Search code examples
androidgradientandroid-jetpack-compose

Gradient over image in Jetpack Compose


Here is my component:

@Composable
fun Cover(
    name: String,
    imageRes: Int,
    modifier: Modifier = Modifier.padding(16.dp, 8.dp)
) {
    Box(modifier) {
        Card(
            shape = RoundedCornerShape(4.dp),
            backgroundColor = MaterialTheme.colors.secondary,
            elevation = 4.dp
        ) {
            Stack {
                Image(
                    imageResource(imageRes),
                    modifier = Modifier
                        .gravity(Alignment.Center)
                        .aspectRatio(2f),
                    contentScale = ContentScale.Crop,
                )

                Text(
                    text = name,
                    modifier = Modifier
                        .gravity(Alignment.BottomStart)
                        .padding(8.dp),
                    style = MaterialTheme.typography.h6
                )
            }
        }
    }
}

This is how it looks:

cover

I want to display a dark gradient over the Image and behind the Text so that the text is easy to read. I guess I'll have to use LinearGradient or RadialGradient but due to the lack of documentation I'm not able to do it.

Edit: This is what I'm trying to do but with Jetpack Compose.


Solution

  • Compose version 1.2.1 as of 2022-08-22

    import androidx.annotation.DrawableRes
    import androidx.compose.foundation.Image
    import androidx.compose.foundation.background
    import androidx.compose.foundation.layout.Box
    import androidx.compose.foundation.layout.fillMaxWidth
    import androidx.compose.foundation.layout.padding
    import androidx.compose.foundation.shape.RoundedCornerShape
    import androidx.compose.material3.Card
    import androidx.compose.material3.Text
    import androidx.compose.runtime.Composable
    import androidx.compose.ui.Alignment
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.graphics.Brush
    import androidx.compose.ui.graphics.Color
    import androidx.compose.ui.layout.ContentScale
    import androidx.compose.ui.res.painterResource
    import androidx.compose.ui.tooling.preview.Preview
    import androidx.compose.ui.unit.dp
    
    @Composable
    fun Cover(
        name: String,
        @DrawableRes imageRes: Int,
        modifier: Modifier = Modifier
    ) {
        Box(modifier.padding(16.dp, 8.dp)) {
            Card(shape = RoundedCornerShape(4.dp)) {
                Box {
                    Image(
                        painter = painterResource(imageRes),
                        contentDescription = "image: $name",
                        modifier = Modifier
                            .align(Alignment.Center),
                        contentScale = ContentScale.Crop
                    )
                    Text(
                        text = name,
                        modifier = Modifier
                            .align(Alignment.BottomStart)
                            .fillMaxWidth()
                            .background(
                                Brush.verticalGradient(
                                    0F to Color.Transparent,
                                    .5F to Color.Black.copy(alpha = 0.5F),
                                    1F to Color.Black.copy(alpha = 0.8F)
                                )
                            )
                            .padding(start = 8.dp, end = 8.dp, bottom = 8.dp, top = 24.dp),
                        color = Color.White
                    )
                }
            }
        }
    }
    
    @Preview
    @Composable
    fun ComicsPreview() {
        Cover(
            "Comics",
            R.drawable.comics
        )
    }
    

    The updated answer of @vitor-ramos

    1.0.0-alpha09

    import androidx.annotation.DrawableRes
    import androidx.compose.foundation.Image
    import androidx.compose.foundation.background
    import androidx.compose.foundation.layout.*
    import androidx.compose.foundation.shape.RoundedCornerShape
    import androidx.compose.material.Card
    import androidx.compose.material.MaterialTheme
    import androidx.compose.material.Text
    import androidx.compose.runtime.Composable
    import androidx.compose.runtime.mutableStateOf
    import androidx.compose.runtime.remember
    import androidx.compose.ui.Alignment
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.graphics.Brush
    import androidx.compose.ui.graphics.Color
    import androidx.compose.ui.layout.ContentScale
    import androidx.compose.ui.layout.onGloballyPositioned
    import androidx.compose.ui.platform.AmbientDensity
    import androidx.compose.ui.res.imageResource
    import androidx.compose.ui.unit.dp
    import tech.abd3lraouf.learn.compose.kombose.ui.theme.typography
    
    @Composable
    fun Cover(
        name: String,
        @DrawableRes imageRes: Int,
        modifier: Modifier = Modifier
    ) {
        val image = imageResource(imageRes)
        val density = AmbientDensity.current.density
        val width = remember { mutableStateOf(0f) }
        val height = remember { mutableStateOf(0f) }
        Box(
            modifier
                .padding(16.dp, 8.dp)
        ) {
            Card(
                shape = RoundedCornerShape(4.dp),
                backgroundColor = MaterialTheme.colors.secondary,
                elevation = 4.dp
            ) {
                Box {
                    Image(
                        image,
                        modifier = Modifier
                            .align(Alignment.Center)
                            .aspectRatio(2f)
                            .onGloballyPositioned {
                                width.value = it.size.width / density
                                height.value = it.size.height / density
                            },
                        contentScale = ContentScale.Crop,
                    )
                    Column(
                        Modifier
                            .size(width.value.dp, height.value.dp)
                            .background(
                                Brush.verticalGradient(
                                    listOf(Color.Transparent, Color.Black),
                                    image.height * 0.6F,
                                    image.height * 1F
                                )
                            )
                    ) {}
                    Text(
                        text = name,
                        modifier = Modifier
                            .align(Alignment.BottomStart)
                            .padding(8.dp),
                        style = typography.body2,
                        color = Color.White
                    )
                }
            }
        }
    }
    
    

    Also, notice the control of how the gradient draws its height.

    The output

    Code output