Search code examples
androidkotlinandroid-jetpack-compose

Transparent stickyHeader shows items behind


I'm looking into a way of having my stickyheader with a transparent background, this is because my background has a gradient and I need the header to have a transparent background.

Reproducible example

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Example() {
    Box(
        Modifier.fillMaxSize()
            .drawBackgroundGradient()
            .clipToBounds()
    ) {
        LazyColumn {
            stickyHeader {
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .background(Color.Transparent)
                        .padding(top = 16.dp, bottom = 16.dp)
                ) {
                    Card(
                        modifier = Modifier.fillMaxWidth(),
                        border = BorderStroke(1.dp, Color.Gray),
                        shape = RoundedCornerShape(20.dp),
                        colors = CardDefaults.cardColors(containerColor = TransparentBackground)
                    ) {
                        Row(
                            modifier = Modifier.padding(12.dp),
                            verticalAlignment = Alignment.CenterVertically
                        ) {

                            Icon(
                                modifier = Modifier.padding(end = 16.dp),
                                painter = rememberVectorPainter(Icons.Filled.Warning),
                                contentDescription = "Disclaimer icon",
                                tint = Color.Yellow
                            )

                            Text(
                                text = "Test copy text of banner",
                                style = TS_Helvetica_400.copy(
                                    lineHeight = 15.sp
                                ),
                                fontSize = 14.sp,
                                color = Color.White
                            )
                        }

                    }

                }
            }
            items(fakeList) { item ->
                Text(modifier = Modifier.fillMaxWidth(), text = item, color = Color.White)
            }
        }
    }
}

val baseList = listOf("name 1", "name 2", "name 3")

// Create a list of 100 elements by repeating baseList
val fakeList = List(100 / baseList.size) { baseList }.flatten() + baseList.take(100 % baseList.size)

fun Modifier.drawBackgroundGradient(): Modifier {
    return this.drawBehind {
        val gradientColors =
            listOf(Color(0xFF6A66FF).copy(alpha = 0.2f), Color.Transparent)

        // Draw the background color
        drawRect(color = Color.Black)

        // Draw the first radial gradient circle
        drawCircle(
            brush = Brush.radialGradient(
                colors = gradientColors,
                center = Offset(size.width * 0.1f, size.height * 0.5f),
                radius = 800.dp.toPx()
            ),
            radius = 800.dp.toPx(),
            center = Offset(size.width * 0.1f, size.height * 0.5f)
        )

        // Draw the second radial gradient circle
        drawCircle(
            brush = Brush.radialGradient(
                colors = gradientColors,
                center = Offset(size.width * 0.9f, size.height * 0.9f),
                radius = 500.dp.toPx()
            ),
            radius = 500.dp.toPx(),
            center = Offset(size.width * 0.9f, size.height * 0.9f)
        )
    }
}

I have been trying some workarounds but they do not include the stickyHeader component, I know it is experimental, but is there a way maybe that the items are not showing behind when dragging but instead cut at some kind of boxed content?


Solution

  • To the best of my knowledge, LazyColumn will always scroll items behind the stickyHeader. You can achieve your desired UI by separating the header and wrapping it in a Column along with LazyColumn.

    Here is the updated code:

    @Composable
    fun Example() {
    Column(
        Modifier
            .fillMaxSize()
            .drawBackgroundGradient()
            .clipToBounds()
    ) {
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .background(Color.Transparent)
                .padding(top = 16.dp, bottom = 16.dp)
        ) {
            Card(
                modifier = Modifier.fillMaxWidth(),
                border = BorderStroke(1.dp, Color.Gray),
                shape = RoundedCornerShape(20.dp),
                colors = CardDefaults.cardColors(containerColor = Color.Transparent)
            ) {
                Row(
                    modifier = Modifier.padding(12.dp),
                    verticalAlignment = Alignment.CenterVertically
                ) {
    
                    Icon(
                        modifier = Modifier.padding(end = 16.dp),
                        painter = rememberVectorPainter(Icons.Filled.Warning),
                        contentDescription = "Disclaimer icon",
                        tint = Color.Yellow
                    )
    
                    Text(
                        text = "Test copy text of banner",
                        fontSize = 14.sp,
                        color = Color.White
                    )
                }
    
            }
    
        }
        LazyColumn(modifier = Modifier.weight(1f)) {
            items(fakeList) { item ->
                Text(modifier = Modifier.fillMaxWidth(), text = item, color = Color.White)
            }
        }
      }
    }