Search code examples
androidkotlinandroid-jetpack-composekotlin-multiplatformcompose-multiplatform

How can I implement a scrollable view with a fixed header in Compose Multiplatform


I am trying to create a layout in Compose Multiplatform where the header remains at the top, while the rest of the content below it is scrollable. The header should be visible at all times, and only the content below should scroll.

I've tried using LazyColumn and Column, but I am having trouble getting the header to stay fixed while scrolling. Is there a recommended way or best practice to achieve this layout in Compose Multiplatform?

Like this

Scrollable content and fixed header


Solution

  • There are two ways to achieve this.

    A) using a Column and a LazyColumn with weight

    Please apply the weight Modifier to the LazyColumn, so that it will fill all remaining screen space left from the header Composable.

    Column(
        modifier = Modifier.fillMaxSize()
    ) {
        Text(
            modifier = Modifier.fillMaxWidth(),
            text = "HEADER",
            fontSize = 32.sp,
            textAlign = TextAlign.Center
        )
        LazyColumn(
            modifier = Modifier
                .weight(1f)
                .fillMaxWidth()
        ) {
            items(100) {
                Text(text = "Item $it")
            }
        }
    }
    

    B) using only a LazyColumn with a stickyHeader

    Alternatively, you can include the header Composable within the LazyColumn as a stickyHeader (experimental):

    @OptIn(ExperimentalFoundationApi::class)
    //...
    LazyColumn(
        modifier = Modifier.fillMaxSize()
    ) {
        stickyHeader {
            Text(
                modifier = Modifier.fillMaxWidth(),
                text = "HEADER",
                fontSize = 32.sp,
                textAlign = TextAlign.Center
            )
        }
        items(100) {
            Text(text = "Item $it")
        }
    }
    

    By default, the stickyHeader has a transparent background. If this is not wanted, you would need to wrap your header Composable with a Surface:

    stickyHeader {
        Surface {
            Text(
                modifier = Modifier.fillMaxWidth(),
                text = "HEADER",
                fontSize = 32.sp,
                textAlign = TextAlign.Center
            )
        }
    }
    

    Output:

    Screenshot


    Note:
    Please also check out the Scaffold Composable, it offers some predefined slots for TopAppBars or BottomBars.