0

I have a Composable function for operating system infos which expands it details upon click and reverts when clicked again.

@ExperimentalAnimationApi
@Composable
fun OSCard(os: OS) {
    var expanded by remember {
        mutableStateOf(false)
    }
    Column(modifier = Modifier
        .clickable { expanded = !expanded }
        .fillMaxWidth()) {
        Text(
            modifier = Modifier
                .padding(20.dp),
            text = os.name,
            style = MaterialTheme.typography.h6
        )
        AnimatedVisibility(visible = expanded) {
            Text(text = os.description, modifier = Modifier.padding(20.dp))
        }
        Divider(Modifier.height(2.dp))
    }
}

I created a list of it and passed it through a LazyColumn

var OSs = listOf<OS>(
    OS(
        "Android",
        "Android is a mobile/desktop operating system..."
      ),
    OS(
        "Microsoft Windows",
        "Microsoft Windows, commonly referred to as Windows..."
      ),
    OS(
        "Linux",
        "Linux is a family of open-source Unix-like operating systems..."
      )
)

Surface(color = MaterialTheme.colors.background) {
    LazyColumn(modifier = Modifier.fillMaxSize()) {
        items(items =  OSs){
            os -> OSCard(os)
        }
    }
}

While it works as expected, I want to make it such that if a card is opened and another card is selected, the previously opened card will be closed.

This is what I am trying to avoid, can someone give me a tip on how to?

What I am trying to avoid

Kinyo356
  • 135
  • 13

1 Answers1

1

Here is one solution. Add a parameter to your OS object called isExpanded. It only gets set to true when you click on a card. The click handler will clear the flag in all the other cards.

I also added an id parameter which makes it easier to find the item being clicked. The name parameter could have been used.

The state variable expandCard needs to read for it to trigger a recompose, so var e = expandCard is used to read the value. Also note that the creation of your list MUST be outside of the composable, otherwise it would just end up getting re-created and the isExpanded field would be set to false on all items.

And lastly, I'm not sure why you're using a LazyColumn for this. LazyColumn is really meant for large datasets and primarily for paging. You could just use a normal Column with vertical scrolling if you have a reasonable number of items.

class MainActivity : ComponentActivity() {
    @ExperimentalAnimationApi
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        var OSs = listOf(
            OS(
                id = "android",
                name = "Android",
                description =  "Android is a mobile/desktop operating system..."
            ),
            OS(
                id = "mswindows",
                name = "Microsoft Windows",
                description = "Microsoft Windows, commonly referred to as Windows..."
            ),
            OS(
                id = "linux",
                name ="Linux",
                description = "Linux is a family of open-source Unix-like operating systems..."
            )
        )

        setContent {

            var expandCard by remember { mutableStateOf(false) }
            var e = expandCard

            Surface() {
                LazyColumn(  modifier = Modifier.fillMaxSize()) {
                    itemsIndexed(
                        items = OSs,
                        key = { index, os ->
                            os.name
                        }
                    ) { index, os ->
                        OSCard(os) {osClicked ->
                            val isExpanded = osClicked.isExpanded
                            OSs.forEach { it.isExpanded = false }
                            OSs.first { it.id == osClicked.id }.isExpanded = isExpanded
                            expandCard = !expandCard
                        }
                    }
                }
            }
        }
    }
}

@ExperimentalAnimationApi
@Composable
fun OSCard(
    os: OS,
    onClick: (os: OS) -> Unit
) {

    Column(modifier = Modifier
        .clickable {
            os.isExpanded = !os.isExpanded
            onClick(os)
        }
        .fillMaxWidth()) {
        Text(
            modifier = Modifier
                .padding(20.dp),
            text = os.name,
            style = MaterialTheme.typography.h6
        )

        AnimatedVisibility(visible = os.isExpanded) {
            Text(text = os.description, modifier = Modifier.padding(20.dp))
        }
        Divider(Modifier.height(2.dp))
    }
}

class OS(var id: String, var name: String, var description: String, var isExpanded: Boolean= false)
Johann
  • 27,536
  • 39
  • 165
  • 279
  • I am so glad for the answer as well as the explanations of what to do and what not. I appreciate the knowledge shared. Thank you. – Kinyo356 Nov 23 '21 at 14:59