is it possible to create Spinner with Compose framework ? Cause I was trying but looks like there is no compose method like for example to create Button :
Button(onClick = {},
modifier = Modifier
.align(Alignment.CenterHorizontally)
.fillMaxWidth()
)
{
Text(text = "Next")
}
Any advice how do create Spinner ? Do I need to use xml ?
Here's an example of how to create a Spinner/ComboBox/Select in Compose. https://gist.github.com/chethu/f078658ef88d138ea92ab773c7396b5d
Based on the answer above referring to the gist, I would like to propose a similar solution just in one composable but accepting an object instead of a list. In the sample I just used a Pair<String, String> that could be replaced by any object.
package at.techbee.jtx.ui.compose.elements
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowDropDown
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@Composable
fun SampleSpinner(
list: List<Pair<String, String>>,
preselected: Pair<String, String>,
onSelectionChanged: (selection: Pair<String, String>) -> Unit
) {
var selected by remember { mutableStateOf(preselected) }
var expanded by remember { mutableStateOf(false) } // initial value
Box {
Column {
OutlinedTextField(
value = (selected.second),
onValueChange = { },
label = { Text(text = "My List") },
modifier = Modifier.fillMaxWidth(),
trailingIcon = { Icon(Icons.Outlined.ArrowDropDown, null) },
readOnly = true
)
DropdownMenu(
modifier = Modifier.fillMaxWidth(),
expanded = expanded,
onDismissRequest = { expanded = false },
) {
list.forEach { entry ->
DropdownMenuItem(
modifier = Modifier.fillMaxWidth(),
onClick = {
selected = entry
expanded = false
},
text = {
Text(
text = (entry.second),
modifier = Modifier.wrapContentWidth().align(Alignment.Start))
}
)
}
}
}
Spacer(
modifier = Modifier
.matchParentSize()
.background(Color.Transparent)
.padding(10.dp)
.clickable(
onClick = { expanded = !expanded }
)
)
}
}
@Preview(showBackground = true)
@Composable
fun SampleSpinner_Preview() {
MaterialTheme {
val entry1 = Pair("Key1", "Entry1")
val entry2 = Pair("Key2", "Entry2")
val entry3 = Pair("Key3", "Entry3")
SampleSpinner(
listOf(entry1, entry2, entry3),
preselected = entry2,
onSelectionChanged = { selected -> /* do something with selected */ }
)
}
}
Hope it helps!
////// EDIT /////
I have been playing around with this and I'm using now a solution that I like more. The Spacer overlay in the previous solution (also as proposed in answers before) is not really good when you would like to put a modifier on the element. So instead of the OutlinedTextField I am using a card in this solution:
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowDropDown
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SpinnerSample(
list: List<MyData>,
preselected: MyData,
onSelectionChanged: (myData: MyData) -> Unit,
modifier: Modifier = Modifier
) {
var selected by remember { mutableStateOf(preselected) }
var expanded by remember { mutableStateOf(false) } // initial value
OutlinedCard(
modifier = modifier.clickable {
expanded = !expanded
}
) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.Top,
) {
Text(
text = selected.name,
modifier = Modifier.weight(1f)
.padding(horizontal = 16.dp, vertical = 8.dp)
)
Icon(Icons.Outlined.ArrowDropDown, null, modifier = Modifier.padding(8.dp))
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier.fillMaxWidth() // delete this modifier and use .wrapContentWidth() if you would like to wrap the dropdown menu around the content
) {
list.forEach { listEntry ->
DropdownMenuItem(
onClick = {
selected = listEntry
expanded = false
onSelectionChanged(selected)
},
text = {
Text(
text = listEntry.name,
modifier = Modifier
//.wrapContentWidth() //optional instad of fillMaxWidth
.fillMaxWidth()
.align(Alignment.Start)
)
},
)
}
}
}
}
}
@Preview(showBackground = true)
@Composable
fun SpinnerSample_Preview() {
MaterialTheme {
val myData = listOf(MyData(0, "Apples"), MyData(1, "Bananas"), MyData(2, "Kiwis"))
SpinnerSample(
myData,
preselected = myData.first(),
onSelectionChanged = { },
modifier = Modifier.fillMaxWidth()
)
}
}
data class MyData (
val id: Int,
val name: String
)
However, the solution with the OutlinedTextField might still be interesting as an AutoCompletion TextField.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With