Home > front end >  State Hoisting in LazyColumn Single Selection. Jetpack Compose
State Hoisting in LazyColumn Single Selection. Jetpack Compose

Time:11-18

I have a LazyColumn with expandable items. And when i click on the item he expands or collapses, but i need that when i click on closed element it opens and others in the list close.(the one that open). So first i moved state of item from remember variable in the item's composable function to the list of dataClass elements. And when i click on item he execute callback that change value of it' selement in the list, but it doesn't recompose.

@Preview
@Composable
fun ExpandableCardList() {
    val list = remember { mutableStateListOf(PointsCard(0),PointsCard(1),PointsCard(2),PointsCard(3),PointsCard(4),PointsCard(5))}
    val state = rememberLazyListState()
    Scaffold { paddingValues ->
        LazyColumn(
            state = state,
            modifier = Modifier
                .fillMaxWidth(),
            contentPadding = paddingValues
        ) {
            items(list, {it.key}) { item ->
                ExpandableCard(
                    expanded = item.state,
                    state = {
                        bool ->  item.state = bool
                    }
                )
            }
        }
    }
}
@Composable
fun ExpandableCard(expanded: Boolean, state: (bool:Boolean) -> Unit ){
    Card(
        Modifier
            .fillMaxWidth()
            .clickable { state(!expanded) }
            .background(Color(Color.BLACK))
    ) {
        Column(Modifier.background(Color(Color.BLACK)))
        {
            Box(
                Modifier
                    .fillMaxWidth()
                    .height(54.dp)
                    .background(Color(Color.BLACK))
            )
            AnimatedVisibility(expanded) {
                Box(
                    Modifier
                        .fillMaxWidth()
                        .padding(start = 10.dp)
                        .heightIn(56.dp, 300.dp)
                        .background(Color(Color.BLACK), shape = RoundedCornerShape(bottomStart = 8.dp)
                        )
                )
            }
       }
    }
}
data class PointsCard(
    val key: Int = 0,
    var state: Boolean = false
)

CodePudding user response:

If you want a single selection, you should probably consider hoisting your list in a class and perform all the structural changes via the list's iterator.

class CardListState {
    val list = mutableStateListOf(PointsCard(0),PointsCard(1),PointsCard(2),PointsCard(3),PointsCard(4),PointsCard(5))

    fun onSelected(isSelected: Boolean, item: PointsCard) {
        val iterator = list.listIterator()
        while (iterator.hasNext()) {
            val obj = iterator.next()
            if (obj.key != item.key) {
                iterator.set(obj.copy(state = false))
            } else {
                iterator.set(obj.copy(state = isSelected))
            }
        }
    }
}

@Composable
fun ExpandableCardList() {

    val cardListState = remember { CardListState() }
    val state = rememberLazyListState()

    Scaffold { paddingValues ->
        LazyColumn(
            state = state,
            modifier = Modifier
                .fillMaxWidth(),
            contentPadding = paddingValues
        ) {
            items(cardListState.list, {it.key} ) { item ->
                ExpandableCard(
                    expanded = item.state,
                    state = { bool ->
                        cardListState.onSelected(bool, item)
                    }
                )
            }
        }
    }
}

@Composable
fun ExpandableCard(
    expanded: Boolean,
    state: (bool:Boolean) -> Unit
) {
    Card(
        Modifier
            .fillMaxWidth()
            .clickable {
                state(!expanded)
            }
            .background(Color.Black)
    ) {
        Column(Modifier.background(Color.Black))
        {
            Box(
                Modifier
                    .fillMaxWidth()
                    .height(54.dp)
                    .background(Color.Red)
            )
            AnimatedVisibility(expanded) {
                Box(
                    Modifier
                        .fillMaxWidth()
                        .padding(start = 10.dp)
                        .heightIn(56.dp, 300.dp)
                        .background(Color.Green)
                )
            }
        }
    }
}

enter image description here

  • Related