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?
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)