How to implement list multiSelect in Jetpack Compose?

5.5k views Asked by At

I need to implement multiselect for LazyList, which will also change appBar content when list items are long clicked.

For ListView we could do that with just setting choiceMode to CHOICE_MODE_MULTIPLE_MODAL and setting MultiChoiceModeListener.

Is there a way to do this using Compose?

enter image description here

3

There are 3 answers

3
Jakoss On

Add selected field to some class representing the item. Then just compose proper code based on that field. In compose you don't have to look for some LazyColumn flag or anything like that. You are in control of the whole state of the list.

The same can be said about the AppBar, you can do a simple if there, like if (items.any { it.selected }) // display button

0
John Ametepe Agboku On

Since you're in control of the whole state in jetpack compose, you can easily create your own multi-select mode. This is an example.

  1. First I created a ViewModel

     val dirs: LiveData<DirViewState> = {
         DirViewState.Content(dirRepository.targetFolders)}.asFlow().asLiveData()
    
     val all: LiveData<DirViewState> = {
         DirViewState.Content(dirRepository.allFolders)
     }.asFlow().asLiveData()
    
     val createFolder = mutableStateOf(false)
    
     val refresh = mutableStateOf(false)
    
     val enterSelectMode = mutableStateOf(false)
    
     val selectedAll = mutableStateOf(false)
    
     val selectedList = mutableStateOf(mutableListOf<String>())
    
    
     fun updateList(path: String){
         if(path in selectedList.value){
             selectedList.value.remove(path)
         }else{
             selectedList.value.add(path)
         }
     }
    

    }

  2. Usage

      Card(modifier = Modifier
         .width(100.dp)
         .height(120.dp)
         .padding(8.dp)
         .pointerInput(Unit) {
             detectTapGestures(
                 onLongPress = { **viewModel.enterSelectMode.value = true** },
                 onTap = {
                     if (viewModel.enterSelectMode.value) {
                         viewModel.enterSelectMode.value = false
                     }
                 }
             )
         }
    
         ,
         shape = MaterialTheme.shapes.medium
     ) {
    
         Image(painter = if (dirModel.dirCover != "") painter
     else painterResource(id = R.drawable.thumbnail),
         contentDescription = "dirThumbnail",
         modifier = Modifier
             .fillMaxHeight()
             .fillMaxWidth(),
         contentScale = ContentScale.FillHeight)
    
         **AnimatedVisibility(
             visible = viewModel.enterSelectMode.value,
             enter = expandIn(),
             exit = shrinkOut()
         ){
             Row(verticalAlignment = Alignment.Top,
                 horizontalArrangement = Arrangement.Start) {
                 Checkbox(checked = isSelected.value, onCheckedChange = {
                     isSelected.value = !isSelected.value
                     viewModel.updateList(dirModel.dirPath)
                 })
             }
         }**
    

    }

0
Bita Mirshafiee On

You can simply handle this by the below code: Fore example This is your list:

        LazyColumn {
        items(items = filters) { item ->
            FilterItem(item)
        }
    }

and this is your list item View:

@Composable
fun FilterItem(filter: FilterModel) {

   val (selected, onSelected) = remember {  mutableStateOf(false) }

   Card(
     shape = RoundedCornerShape(8.dp),
     modifier = Modifier
        .padding(horizontal = 8.dp)
        .border(
            width = 1.dp,
            colorResource(
                if (selected)
                    R.color.black
                else
                    R.color.white
            ),
            RoundedCornerShape(8.dp)
        )) {

    Text(
        modifier = Modifier
            .toggleable(
                value =
                selected,
                onValueChange =
                onSelected
            )
            .padding(8.dp),
    text = filter.text)
}

}

and that's it use tooggleable on your click component modifier like here I did on text.