Hey, my screen lags when scrolling. I'm using a La...
# compose
j
Hey, my screen lags when scrolling. I'm using a LazyColumn. (see thread for code). What could cause this? I'm only displaying text and small icons
Copy code
val shops by viewModel.homeFlow.collectAsState(listOf())
LazyColumn(
    horizontalAlignment = Alignment.CenterHorizontally,
    modifier = Modifier.fillMaxSize()
) {
    shops.forEach { (shop, products) ->
        item {
            Box(
                modifier = Modifier
                    .padding(5.dp)
                    .fillMaxWidth()
                    .animateItemPlacement()
                    .clickable {
                        viewModel.changeShopVisibility(shop.id, !shop.isVisible)
                    },
                contentAlignment = Alignment.Center
            ) {
                Text(text = shop.name, fontSize = 20.sp, fontWeight = FontWeight.Bold)
                IconButton(
                    onClick = {
                        viewModel.changeShopPinned(shop.id, !shop.isPinned)
                    },
                    modifier = Modifier.align(Alignment.CenterEnd)
                ) {
                    if(shop.isPinned) {
                        Icon(LocalIcon.BookmarkAdd, null)
                    } else {
                        Icon(LocalIcon.Bookmark, null, tint = MaterialTheme.colorScheme.surfaceTint)
                    }
                }
            }
        }
        if (shop.isVisible) {
            items(products, { it.id }) {
                ProductEntryCard(it, viewModel)
            }
        }
    }
}
This is the "homeFlow":
Copy code
val homeFlow = shopFlow.combine(productEntryFlow) { shops, products ->
    shops
        .filter { // filter out shops that have no products
            products.any { product -> product.shopId == it.id }
        } // map the shops and products to a pair
        .sortedBy { it.isPinned }
        .map { it to products.filter { product -> product.shopId == it.id } }
}
    .flowOn(Dispatchers.Default)
c
Why did you use for loop for LazyColumn? You can use
items
instead
j
Because I kinda have to loop through it twice (see
Copy code
if (shop.isVisible) {
            items(products, { it.id }) {
                ProductEntryCard(it, viewModel)
            }
        }
) And I didn't know how I'd do that
I mean I'd have to use a loop for this instead, no?
s
It looks correct, it's just a bit jarring to see a for loop inside a
LazyColumn
. Does it lag when compiled for release? Compose is notorious for bad performance in debug builds. If it does, I would take a look at
ProductEntryCard
to see if that's the culprit.
Also, how many shops are there? It might be doing quite a bit of work to build that list. If you have many more shops than products/shop, you could try making a shop with all of its products a single item.
j
~ 5 shops with each 2 items (just for testing purposes)
Some general advice on performance tracking for Jetpack Compose.
o
Copy code
ProductEntryCard(it, viewModel)
ViewModel is not Stable. Do you really want to pass viewModel down the composable hierarchy? Try a pure data class and see if the performance improves
t
Did you tried to use the
items
interface from LazyColumn not
item
? Maybe this could improve your performance
Copy code
LazyColumn() {
    items(shops.entries) { (shop, products) ->
        ....
j
Then I'd still have to use a second loop for the products
After doing this: • Don't pass down the view model to the products itself • Use
items
for shops instead of products (and use a loop for the products) It's definitely smoother, but there are still some annoying lags.
It currently looks like this:
Copy code
val shops by viewModel.homeFlow.collectAsState(listOf())
LazyColumn(
    horizontalAlignment = Alignment.CenterHorizontally,
    modifier = Modifier.fillMaxSize()
) {
    items(shops, { it.first.id }) { (shop, products) ->
        Box(
            modifier = Modifier
                .padding(5.dp)
                .fillMaxWidth()
                .animateItemPlacement()
                .clickable {
                    viewModel.changeShopVisibility(shop.id, !shop.isVisible)
                },
            contentAlignment = Alignment.Center
        ) {
            Text(text = shop.name, fontSize = 20.sp, fontWeight = FontWeight.Bold)
            IconButton(
                onClick = {
                    viewModel.changeShopPinned(shop.id, !shop.isPinned)
                },
                modifier = Modifier.align(Alignment.CenterEnd)
            ) {
                if (shop.isPinned) {
                    Icon(LocalIcon.BookmarkAdd, null)
                } else {
                    Icon(
                        LocalIcon.Bookmark,
                        null,
                        tint = MaterialTheme.colorScheme.surfaceTint
                    )
                }
            }
        }
        Column {
            products.forEach { product ->
                ProductEntryCard(
                    product = product,
                    onDoneChange = {
                        if (it) {
                            viewModel.markEntryAsDone(product.id)
                        } else {
                            viewModel.markEntryAsNotDone(product.id)
                        }
                    }, onDelete = {
                        viewModel.deleteEntry(product.id)
                    }
                )
            }
        }
    }
}
And using the release mode didn't help
Funnily enough, I have a different screen displaying images in a LazyColumn, which doesn't lag at all. But that also only uses
items
and no loop. But I have no idea how I'd improve that.
s
Perhaps LazyList is not the cause, if it works fine on a different screen. And some slow code is causing trouble here. Use the profiler, it will help you find the code that is causing problems. Follow the official guides. https://developer.android.com/jetpack/compose/tooling/tracing

https://www.youtube.com/watch?v=Kp-aiSU8qCU

502 Views