13

I am struggling with the jetpack compose LazyColumn and the stickyHeader functionality. Basically the static view works well, but once I start scrolling, the items would go over the sticky headers, the scrolling starts a weird behaviour and the last item would never be visible as the scrolling always bounces back.

Here's how it looks like:

enter image description here

Here's the composable:

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun CollectionsScreen(
    collectionsLive: LiveData<List<CollectionsView>>,
    onCollectionChanged: (ICalCollection) -> Unit
    /* some more hoisted functions left out for simplicity */
) {

    val list by collectionsLive.observeAsState(emptyList())
    val grouped = list.groupBy { it.accountName ?: it.accountType ?: "Account" }

    LazyColumn(
        modifier = Modifier.padding(8.dp)
    ) {

        item {
            Text(
                stringResource(id = R.string.collections_info),
                textAlign = TextAlign.Center,
                modifier = Modifier.padding(bottom = 16.dp)
            )
        }

        grouped.forEach { (account, collectionsInAccount) ->
            stickyHeader {
                Text(
                    account,
                    style = MaterialTheme.typography.titleLarge,
                    fontWeight = FontWeight.Bold,
                    modifier = Modifier.padding(
                        top = 16.dp,
                        start = 8.dp,
                        end = 16.dp,
                        bottom = 8.dp
                    )
                )
            }

            items(
                items = collectionsInAccount,
                key = { collection -> collection.collectionId }
            ) { collection ->

                CollectionCard(
                    collection = collection,
                    allCollections = list,
                    onCollectionChanged = onCollectionChanged,
                    /* some more hoisted functions left out for simplicity */
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(bottom = 8.dp)
                        .animateItemPlacement()
                        .combinedClickable(
                            //onClick = { onCollectionClicked(collection) }
                         )
                )
            }
        }
    }
}

I am really not sure what is causing this issue as the code itself is pretty straightforward from the example provided in the documentation. Only the CollectionCard itself is a more complex structure. I have also tried removing the header text (the first item) and removed the Modifier.animateItemPlacement() for the card, but with no difference, the problem stays the same... The composable itself is used in a Compose View within a Fragment, but there is no nested scrolling. Do you have any idea what could cause this strange behaviour? Or might this be a bug when using cards within the LazyColumn with sticky headers?

UPDATE: It seems like the problem nothing to do with the stickyHeader, but somehow with the LazyColumn. If I replace the "stickyHeader" just with "item", the problem still persists... Only when I replace the lazyColumn with a column it would work. But I assume that there must be a solution for this problem...

Patrick Lang
  • 501
  • 6
  • 9
  • I am not sure but may be due that for loop your compose function recomposing which is very bad for performance – AgentP Jun 13 '22 at 17:54
  • 1
    Well, loops should actually not be an issue in compose... The code follows the provided example from the documentation: https://developer.android.com/jetpack/compose/lists#sticky-headers – Patrick Lang Jun 17 '22 at 11:14
  • I have the exact same issue, did you figure out any solution for this? – Captain Allergy Aug 15 '22 at 08:25
  • 1
    Add background color to that sticky header – Tippu Fisal Sheriff Oct 07 '22 at 12:50
  • I'd suggest creating an issue on the issuetracker in addition to whatever approach you decide to use. stickyHeader without a default background just seems wrong. Last time I checked there was no issue for it yet. – Uli Feb 03 '23 at 10:11

5 Answers5

7

In general, if you are using Material or Material3 theming, you can wrap your stickyHeader content in a Surface to automatically make it non-transparent with your theme's standard (or customized) coloring scheme. Surface lets you raise the stickyHeader above your table's other contents.

stickyHeader {
    Surface(Modifier.fillParentMaxWidth()) {
        Text("Header")
    }
}

You can customize the Surface at your heart's desire.

I'd create another issue for the bounciness problem, it looks like a separate concern.

Uli
  • 2,828
  • 20
  • 23
2

Setting the stickyHeader background color will help.

stickyHeader {
                Text(
                    "text",
                    modifier = Modifier.padding(
                        top = 16.dp,
                        start = 8.dp,
                        end = 16.dp,
                        bottom = 8.dp
                    )
                   .background(colorResource(id = R.color.white))
                )
            }
halfer
  • 19,824
  • 17
  • 99
  • 186
JL Eric
  • 29
  • 1
0

I don't know if you solved it yet, but try to fillMaxWidth and set the background. This code worked for me.

Text(
          account,
          style = MaterialTheme.typography.titleLarge,
          fontWeight = FontWeight.Bold,
          modifier = Modifier
              .padding(
                       top = 16.dp,
                       start = 8.dp,
                       end = 16.dp,
                       bottom = 8.dp
                      )
              .fillMaxWidth()
              .background(MaterialTheme.colors.background)
    )
0

Just provide a background for the sticky header.

  • Instead of simply providing the answer directly, try writing a detailed comment that explains the solution, as long as the explanation is not too lengthy. @Prashant Singh. – DSDmark Feb 02 '23 at 13:20
-2

You just could replace stickyHeader{} for item{}, you will have the expected result