Search code examples
kotlindrop-down-menuandroid-jetpack-compose

Reusing Dropdown Menu in Kotlin Jetpack Compose


I am creating an application and I would like to have it extend as needed. I want to use a dropdown list to select an option, then show a new dropdown using the same list minus the previous value selected (so you can't double select it), and repeat on and on. So you could have one option selected, with one dropdown under it. But if I select an option in the second dropdown, I want it to show a third box and so on.

I want to use the same list each time so I have it set as follows:

val options = rememberSaveable {
    listOf("Song1", "Song2", "Song3", "Song4",
        "Song5", "Song6", "Song7", "Song8",
        "Song9", "Song10", "Song11", "Song12",
        "Song13", "Song14", "Song15", "Song16", "Song17")
}

I just want to be able to fill box after box as needed, without the chance of double entries.

Example: Dropdown 1 shows: 1-17. I select Song1 Dropdown 2 shows: 2-17. I select Song4 Dropdown 3 shows: 2,3,5-17. I select Song12 Dropdown 4 shows: 2,3,5-11,13-17. etc...

I can't seem to find any documentation on a way to do this in Kotlin specifically. I am not sure what to do to accomplish this.


Solution

  • Have a look at the filter function:

    When called with a predicate, filter() returns the collection elements that match it.

    At some point, you probably have a loop to generate the DropdownMenuItems within the DropdownMenu:

    var selectedOptionA by remember { mutableStateOf("") }
    var selectedOptionB by remember { mutableStateOf("") }
    var selectedOptionC by remember { mutableStateOf("") }
    
    DropdownMenu(
        //... 
    ) {
        options.forEach { option: String ->
            DropdownMenuItem(
                text = { Text(option) },
                onClick = {
                    selectedOptionA = option
                }
            )
        }
    }
    

    Now, in the second DropdownMenu, try it like this:

    options.filter{ 
        it != selectedOptionA  // exclude Song selected in Menu A
    }.forEach { option: String ->
        DropdownMenuItem(
            text = { Text(option) },
            onClick = {
                selectedOptionB = option
            }
        )
    }
    

    Finally, filter both previously selected values from the third DropdownMenu:

    options.filter{ 
        it != selectedOptionA && it != selectedOptionB  // exclude Song selected in Menu A and B
    }.forEach { option: String ->
        DropdownMenuItem(
            text = { Text(option) },
            onClick = {
                selectedOptionC = option
            }
        )
    }
    

    Note that with this approach, after you selected the third item, you can go back to the first DropdownMenu and select the same item as the third one. Adjust the filtering condition if you want to avoid this.