Home > Blockchain >  Why won't the recomposition trigger for my LazyColumn
Why won't the recomposition trigger for my LazyColumn

Time:12-01

I have a LazyColumn which has multiple lists which it's supposed to display depending on the index value. However, when I change the index the list changes, but the items do not get redrawn until I scroll down and back up. I've tossed around the remember keyword, changed my logic N times and it still won't update. Here are my classes

    @Composable
fun MainContent() {
    val state = homeViewModel.state.collectAsState(initial = HomepageState.Loading)
    Theme(config = config) {
        when (state.value) {
            is HomepageState.Loading -> Box(
                modifier = Modifier.fillMaxSize(),
                contentAlignment = Alignment.Center
            ) { CircularProgressIndicator() }
            is HomepageState.Multi -> with(state.value as HomepageState.Multi) {
                updateHomepageImagePreference(index)
                LazyColumnContent(homepage = items, switcher, logo, index)
            }
        }
    }
}

The homepage[index] part is the one I would like to trigger recomposition. I've tried to pass the correct list instead of changing the index, but the result is the same

@Composable
private fun LazyColumnContent(
    homepage: List<List<ModuleConfig>>,
    switcher: HomepageSwitcherTheme?,
    logo: HomepageThemeNavLogo?,
    index: Int = 0
) {
    LaunchedEffect(key1 = index) {
        updateHomepageSwitcher(switcher)
        updateNavigationBarLogo(logo)
    }
    return LazyColumn(
        modifier = Modifier
            .fillMaxSize()
            .background(vennConfig.themeConfig.backgroundColor?.color)
    ) {
        itemsIndexed(homepage[index]) { _, item ->
            AndroidView(
                modifier = Modifier.fillMaxSize(),
                factory = {
                    val productsInCategoryCriteriaSatisfied =
                        if (item.requiresProductsInCategoryId.isNullOrEmpty()) true
                        else categoryHasProducts[item.requiresProductsInCategoryId] ?: true

                    return@AndroidView if (productsInCategoryCriteriaSatisfied) moduleFactory.build(
                        item,
                        requireContext()
                    )
                    else View(context) // blank view
                }
            )
        }
    }
}

I'm guessing I'm doing something wrong with my Compose usage, but I can't figure out what.

CodePudding user response:

AndroidView factory is only getting called once the view appears. If you need to update item on the same view, you can use update. Also you don't need to create an empty view when you have nothing to show, just create AndroidView on demand:

val productsInCategoryCriteriaSatisfied =
    if (item.requiresProductsInCategoryId.isNullOrEmpty()) true
    else categoryHasProducts[item.requiresProductsInCategoryId] ?: true

if (productsInCategoryCriteriaSatisfied) {
    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { context ->
            moduleFactory.build(
                item,
                context
            )
        },
        update = {
            it.item = item
        },
    )
}

An other problem is that you may have change the items order.

The cleanest way of solving this problem is using key parameter: by default it's equal item index.

Perfectly your item have an id, but also you can build the key depending on any other item properties. When the key is not the same since the last recomposition - AndroidView will be re-created. p.s. when you don't need index, you can use items instead of itemsIndexed:

LazyColumn {
    items(list, key = { it.id }) { item ->

    }
}
  • Related