I have a screen in which users can see recommendations saved by them from different categories with filter buttons above implemented with the help of FlowRow:
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun SavedScreen(
savedRecommendationsMap: Map<Pair<CATEGORY, Boolean>, List<Recommendation>?>,
onClick: (Recommendation, CATEGORY) -> Unit,
onFilterButtonClick: (category: CATEGORY, isSelected: Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
Rebugger(
trackMap = mapOf(
"savedRecommendationsMap" to savedRecommendationsMap,
"onClick" to onClick,
"onFilterButtonClick" to onFilterButtonClick,
"modifier" to modifier,
),
)
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.padding(dimensionResource(R.dimen.padding_medium))
.fillMaxSize(),
) {
FlowRow(
horizontalArrangement = Arrangement.Start,
verticalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.padding_small)),
modifier = Modifier.fillMaxWidth(),
) {
savedRecommendationsMap.forEach() { (key, value) ->
Log.d(
"SavedScreen",
"Category: ${key.first}, isSelected: ${key.second}, recommendations: ${value?.size ?: 0}"
)
FilterButton(
onFilterButtonClick = onFilterButtonClick,
filterName = key.first,
isFilterSelected = key.second,
modifier = Modifier.padding(end = dimensionResource(R.dimen.padding_small)),
)
}
}
HorizontalDivider(
thickness = 2.dp,
modifier = Modifier.padding(vertical = dimensionResource(R.dimen.padding_medium)),
)
if (!savedRecommendationsMap.any { it.value != null }) {
NothingScreen(modifier = modifier)
} else {
LazyColumn(
modifier = Modifier
.fillMaxSize(),
) {
item {
savedRecommendationsMap.forEach { (key, value) ->
if (key.second == true) {
value?.forEach {
SavedScreenCard(
recommendation = it,
onClick = onClick,
category = key.first,
)
}
}
}
}
}
}
}
}
On the left is the preview. It works fine, but when on a device (right) as you can see only 2 buttons are appearing. Moreover when I tried to find a fix it I came across another issue that this composable is getting recomposed four times. I used Rebugger and used logged to find the reason but I am still unable to fix it.
Logs:
Category: COFFEESHOPS, isSelected: true, recommendations: 0
Category: RESTRAUNTS, isSelected: true, recommendations: 0
Category: PARKS, isSelected: true, recommendations: 0
Category: SHOPPINGCENTERS, isSelected: true, recommendations: 0
Category: HOTELS, isSelected: true, recommendations: 0
????Rebugger recomposed because
`onClick` changed from `Function2<com.example.prguetravel.model.Recommendation, com.example.prguetravel.utils.CATEGORY, kotlin.Unit>` to `Function2<com.example.prguetravel.model.Recommendation, com.example.prguetravel.utils.CATEGORY, kotlin.Unit>`,
`onFilterButtonClick` changed from `Function2<com.example.prguetravel.utils.CATEGORY, java.lang.Boolean, kotlin.Unit>` to `Function2<com.example.prguetravel.utils.CATEGORY, java.lang.Boolean, kotlin.Unit>`,
`modifier` instance changed, but content remains the same -> `androidx.compose.foundation.layout.PaddingValuesElement@e5200000`
Category: COFFEESHOPS, isSelected: true, recommendations: 0
Category: RESTRAUNTS, isSelected: true, recommendations: 0
Category: PARKS, isSelected: true, recommendations: 0
Category: SHOPPINGCENTERS, isSelected: true, recommendations: 0
Category: HOTELS, isSelected: true, recommendations: 0
???? Rebugger activated on `Rebugger`
Category: COFFEESHOPS, isSelected: true, recommendations: 0
Category: RESTRAUNTS, isSelected: true, recommendations: 0
Category: PARKS, isSelected: true, recommendations: 0
Category: SHOPPINGCENTERS, isSelected: true, recommendations: 0
Category: HOTELS, isSelected: true, recommendations: 0
????Rebugger recomposed because
`onClick` changed from `Function2<com.example.prguetravel.model.Recommendation, com.example.prguetravel.utils.CATEGORY, kotlin.Unit>` to `Function2<com.example.prguetravel.model.Recommendation, com.example.prguetravel.utils.CATEGORY, kotlin.Unit>`,
`onFilterButtonClick` changed from `Function2<com.example.prguetravel.utils.CATEGORY, java.lang.Boolean, kotlin.Unit>` to `Function2<com.example.prguetravel.utils.CATEGORY, java.lang.Boolean, kotlin.Unit>`,
`modifier` instance changed, but content remains the same -> `androidx.compose.foundation.layout.PaddingValuesElement@e5200000`
Category: COFFEESHOPS, isSelected: true, recommendations: 0
Category: RESTRAUNTS, isSelected: true, recommendations: 0
Category: PARKS, isSelected: true, recommendations: 0
Category: SHOPPINGCENTERS, isSelected: true, recommendations: 0
Category: HOTELS, isSelected: true, recommendations: 0
Category: COFFEESHOPS, isSelected: true, recommendations: 0
Category: RESTRAUNTS, isSelected: true, recommendations: 0
Category: PARKS, isSelected: true, recommendations: 0
Category: SHOPPINGCENTERS, isSelected: true, recommendations: 0
Category: HOTELS, isSelected: true, recommendations: 0
2
The FlowRow
seems incomplete because the SavedScreen
Composable is being hidden behind the TopAppBar. You are probably using a Scaffold
at some point. Make sure that you are applying the padding that the Scaffold provides to the content
:
Scaffold(
topBar = {
//...
},
content = { innerPadding ->
Box(
modifier = Modifier.padding(innerPadding)
) {
//...
}
}
)
Before we can look into any recomposition analysis:
You are iteratively adding new elements to a single LazyColumn item
by using forEach
. This will this make the LazyColumn itself pointless, as it will always contain only one single item.
I don’t know what exactly you want to achieve, but if you want to show each SavedScreenCard
as a single item in the LazyColumn, please consider to update your code as follows:
LazyColumn(
modifier = Modifier.fillMaxSize(),
) {
savedRecommendationsMap.forEach { (key, value) ->
if (key.second == true) {
value?.forEach { recommendation ->
item {
SavedScreenCard(
recommendation = recommendation,
onClick = onClick,
category = key.first,
)
}
}
}
}
}
This will result in a proper LazyColumn usage, where only the items that are currently visible (and a few more) are composed, and the other items are lazily composed when scrolling.
1