LazyColumn items mutual exclusive
Asked Answered
D

3

1

Say I have a list of items in a LazyColumn and they can all be selected. How can I make sure when an item is selected, the others are deselected (so only 1 item can be selected at once)?

I was thinking about interactionSource but I'm not really sure how use it.

Dotti answered 22/10, 2021 at 7:55 Comment(1)
It depends on how you store the selected state, please add some code.Ainu
P
3

I'd extract this logic into a ViewModel, with your items being a specific UIModel that contains your current data of the items + their state, containing the isSelected state.

For ex:

data class YourUiModel(val id: Long, val isSelected: Boolean = false, ...)

private val _items = MutableStateFlow(emptyList<YourUiModel>())
val items: StateFlow<List<YourUiModel>>
    get() = _items
    
private fun changeSelectionTo(uiModel: YourUiModel) {
        _items.value = items.value.map { it.copy(isSelected = it.id == uiModel.id) }
}

...
Parallel answered 22/10, 2021 at 8:5 Comment(0)
B
2

Just store a variable holding the index of the selected variable

var sel by mutableStateOf(0)

Now you may want to use remember here if you are declaring it in your Composables, but I would highly recommend storing it inside a viewmodel. A simple example would be

class VM: ViewModel(){
 var sel by mutableStateOf(0) //Assuming 0 is selected initially, change it to -1 if you please

 //Create a setter optionally, for state-hoisting
 fun setSel(index: Int){
   sel = index
 }
}

In the MainActivity,

class MainActivity{
 val vm by viewmodels<VM>()
 setContent{
  MySelectableList(
     list = ...,
     selected = vm.sel,
     onSelectionChange = vm::setSel // Just pass the viewmodel if you did not create the setter
   )
 }

 @Composable
 fun MySelectableList(
    list: List<Any>,
    selected: Int,
    onSelectionChange: (int) -> Unit // or vm, if you did not create the setter
  ){
     list.forEachIndexed {index, item ->
       // Now create this Composable yourself
       ItemComposable(
          isSelected = index == selected,
          onClick = { onSelectionChange(index) } // Just focus and have a look at what's happening here
       )
     }
  }
}

I am using state-hoisting here, read this for reference. Def take this codelab if you haven't already

You will need to do an additional check if you set the initial value of sel to -1 earlier. Completely understand the code that is flowing through here.

Blindfold answered 22/10, 2021 at 10:20 Comment(0)
V
1

You can try use remember {...}

@Composable
fun MyComposeList(data: List<MyItemData>,...) {
    val (selectedItem, setSelectedItem) = remember { mutableStateOf(null // or maybe data.first() } // remember current selected by state delegate

    lazyColumn() { // your lazy column
       items (data) { item -> // item = each item
          // your compose item view
          MyComposeItemView (
              data = item,  // pass data by each item
              isSelected = data == selectedItem // set is item selected by default
          ) {
              // on item click
              setSelectedItem(it)
           }
       }
    }
}

@Composable
fun MyComposeItemView(
        data: MyItemData, 
        isSelected: Boolean, 
        onSelect: (MyItemData) -> Unit = {}) {
   
   // here set item selected state = isSelected maybe change color or something

  // assume we have box as single item
  Box(Modifier.clickable {
     onSelect(data) // cal on click function and pass current data as parameter
  })
}
Vasculum answered 22/10, 2021 at 9:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.