I'm writing example-screen with using lazyColumn. I encountered some performance issues on release build. Frame skipping happens when I fast-scroll the list.
All models and composables are stable. My code is below;
Screen record link -> https://imgur.com/a/cvlA8g0
viewModel:
#HiltViewModel
class ExampleViewModel #Inject constructor(
private val repo: ExampleRepository,
) : ViewModel() {
private val _viewState = MutableStateFlow(ItemsViewState())
val viewState = _viewState.asStateFlow()
init {
fetch()
}
private fun fetch() = viewModelScope.launch {
repo.getItems()
.onStart { _viewState.value = _viewState.value.copy(state = PageState.Loading) }
.onCompletion { _viewState.value = _viewState.value.copy(state = PageState.Content) }
.collect { _viewState.value = _viewState.value.copy(items = it.toImmutableList()) }
}
}
viewState and models:
data class ItemsViewState(
val items: ImmutableList<Item> = persistentListOf(),
val state: PageState = PageState.Loading,
)
data class Item(
val id: Int,
val imageUrl: String,
val name: String,
val rating: Double,
val campaignText: String,
val isChecked: Boolean = false,
)
data class ItemViewState(val item: Item) {
fun isRatingVisible(): Boolean = item.rating > 7.0
}
sealed class PageState {
object Content : PageState()
object Loading : PageState()
object Error : PageState()
}
and my composable functions:
#Composable
fun ExampleScreen(
viewModel: ExampleViewModel = hiltViewModel(),
) {
val viewState by viewModel.viewState.collectAsState()
when (viewState.state) {
PageState.Content -> {
ExampleList(viewState = viewState)
}
PageState.Loading -> LoadingScreen()
PageState.Error -> {}
}
}
#Composable
private fun ExampleList(
viewState: ItemsViewState,
) {
LazyColumn(
state = rememberLazyListState(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.fillMaxSize()
) {
items(viewState.items, key = { it.id }) { item ->
ExampleListItem(item = item)
}
}
}
#Composable
private fun ExampleListItem(item: Item) {
val viewState = ItemViewState(item)
Card(
shape = RoundedCornerShape(8.dp),
backgroundColor = MaterialTheme.colors.background
) {
Row(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth()
.wrapContentHeight()
) {
AsyncImage(
model = item.imageUrl,
contentDescription = viewState.item.name,
contentScale = ContentScale.FillHeight,
modifier = Modifier
.clip(RoundedCornerShape(10.dp))
.width(120.dp)
.height(120.dp),
)
Spacer(modifier = Modifier.width(8.dp))
Column(verticalArrangement = Arrangement.SpaceBetween) {
Row(horizontalArrangement = Arrangement.SpaceEvenly) {
Text(
text = viewState.item.name,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
modifier = Modifier.weight(1f),
)
Icon(imageVector = Icons.Default.List, contentDescription = null)
}
Spacer(modifier = Modifier.height(2.dp))
Row {
if (viewState.isRatingVisible()) {
Spacer(modifier = Modifier.width(4.dp))
Text(
text = viewState.item.rating.toString(),
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
}
}
Spacer(modifier = Modifier.height(2.dp))
CampaignRow(campaignText = viewState.item.campaignText)
}
}
}
}
#Composable
private fun CampaignRow(
modifier: Modifier = Modifier,
campaignText: String,
) = Row(modifier = modifier) {
Image(
painter = painterResource(androidx.ui.livedata.R.drawable.abc_ic_star_black_16dp),
contentDescription = "",
modifier = Modifier
.wrapContentSize()
.align(Alignment.CenterVertically)
.padding(end = 4.dp)
)
Text(
text = campaignText,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
}
I followed google guideline to fix performance issues.
I created Baseline profile
I tested on release build
I used key parameter on lazyColumn
but still happening performance issues. How can I prevent this?
I want to show a circular progress bar while the shopping list items are being retrieved from the database. I have a Lazycolumn that displays the retrieved shopping list items, but the circular progress bar is never displayed, and the message "You don't have any items in this shopping list." is displayed briefly before the list is shown. This behavior is not desired. In the viewmodel, placing loading.value = false after the database call in the viewModelScope coroutine does not work. How can I fix this?
ShoppingListScreen Composable
fun ShoppingListScreen(
navController: NavHostController,
shoppingListScreenViewModel: ShoppingListScreenViewModel,
sharedViewModel: SharedViewModel
) {
val scope = rememberCoroutineScope()
val focusManager = LocalFocusManager.current
val screenHeight = LocalConfiguration.current.screenHeightDp.dp
val allItems = shoppingListScreenViewModel.shoppingListItemsState.value?.collectAsLazyPagingItems()
val showProgressBar = shoppingListScreenViewModel.loading.value
Scaffold(
topBar = {
CustomAppBar(
title = "Shopping List Screen",
titleFontSize = 20.sp,
appBarElevation = 4.dp,
navController = navController
)
},
floatingActionButton = {
FloatingActionButton(
onClick = {
shoppingListScreenViewModel.setStateValue(SHOW_ADD_ITEM_DIALOG_STR, true)
},
backgroundColor = Color.Blue,
contentColor = Color.White
) {
Icon(Icons.Filled.Add, "")
}
},
backgroundColor = Color.White,
// Defaults to false
isFloatingActionButtonDocked = false,
bottomBar = { BottomNavigationBar(navController = navController) }
) {
Box {
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.height(screenHeight)
) {
if (allItems?.itemCount == 0) {
item { Text("You don't have any items in this shopping list.") }
}
items(
items = allItems!!,
key = { item ->
item.id
}
) { item ->
ShoppingListScreenItem(
navController = navController,
item = item,
sharedViewModel = sharedViewModel
) { isChecked ->
scope.launch {
shoppingListScreenViewModel.changeItemChecked(item!!, isChecked)
}
}
}
item { Spacer(modifier = Modifier.padding(screenHeight - (screenHeight - 70.dp))) }
}
ConditionalCircularProgressBar(isDisplayed = showProgressBar)
}
}
}
ShoppingListScreenViewModel
#HiltViewModel
class ShoppingListScreenViewModel #Inject constructor(
private val getAllShoppingListItemsUseCase: GetAllShoppingListItemsUseCase
) {
private val _shoppingListItemsState = mutableStateOf<Flow<PagingData<ShoppingListItem>>?>(null)
val shoppingListItemsState: State<Flow<PagingData<ShoppingListItem>>?> get() = _shoppingListItemsState
val loading = mutableStateOf(false)
init {
loading.value = true
getAllShoppingListItemsFromDb()
}
private fun getAllShoppingListItemsFromDb() {
viewModelScope.launch {
_shoppingListItemsState.value = getAllShoppingListItemsUseCase().distinctUntilChanged()
loading.value = false
}
}
}
put the CircularProgressIndicator inside a Surface or Box, maybe you are using Scaffold with navigation, in my case I was using padding top 60.dp in my circular progrees then it was kind of hinding.
I am trying to set an alarm using a TimePicker but the alarm ignores the time sent by parameter and instead it is executed immediately !!
This is my code:
class MainActivity : ComponentActivity() {
lateinit var navController: NavHostController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
navController = rememberNavController()
NotePadReminderTheme {
SetupNavGraph(navController = navController)
}
}
}
}
#RequiresApi(Build.VERSION_CODES.M)
fun createAlarm(context: Context,time:Long){
val alarmManager =context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
val intent =Intent(context, AlarmReceiver::class.java)
Log.d("receivedtime","time $time")
val pendingIntent = PendingIntent.getBroadcast(context, Date().seconds, intent, PendingIntent.FLAG_IMMUTABLE)
alarmManager?.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, pendingIntent)
Toast.makeText(context,"Alarma creada",Toast.LENGTH_LONG).show()
}
#RequiresApi(Build.VERSION_CODES.M)
#Composable
fun AddPage(navController: NavController, vm: TaskViewModel = viewModel()) {
val scope= rememberCoroutineScope()
val title = remember {
mutableStateOf("")
}
val description = remember {
mutableStateOf("")
}
val date = remember {
mutableStateOf("")
}
val time = Calendar.getInstance()
val iscompleted = remember {
mutableStateOf(false)
}
val context = LocalContext.current
Column(
modifier = Modifier
.fillMaxSize()
.padding(top = 30.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Agregar Tarea",
style = NoteTheme.typography.h1
)
RowOfThis(title, "Título", true)
RowOfThis(description, "Descripción", false)
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
DatePick(date)
}
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
TimePick(time)
}
Button(
onClick = {
if (title.value.isNullOrBlank() || description.value.isNullOrBlank() ||
date.value.isNullOrBlank()
) {
Log.d("date","date:${date.value}")
Toast.makeText(context, "Por favor ingrese todos los datos", Toast.LENGTH_LONG)
.show()
return#Button
}
vm.task.value.title = title.value
vm.task.value.description = description.value
vm.task.value.date = date.value
vm.task.value.time = time.timeInMillis
Log.d("tmepickerreceived","date:${time.timeInMillis}")
scope.launch {
vm.createTask(context, vm.task.value)
createAlarm(context, time=time.timeInMillis). //here set the alarm ****************
Toast.makeText(context, "Tarea creada correctamente", Toast.LENGTH_LONG).show()
vm.resetTask()
}
},
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Blue)
) {
Text(text = "Guardar", color = Color.White, style = NoteTheme.typography.subtitle)
}
}
}
#Composable
fun RowOfThis(value: MutableState<String>, label: String, single: Boolean) {
val focusManager = LocalFocusManager.current
Row(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
) {
TextField(
modifier = Modifier
.fillMaxWidth()
.border(BorderStroke(width = 1.dp, color = Color.Black)),
singleLine = single,
maxLines = 3,
textStyle = NoteTheme.typography.body,
value = value.value, onValueChange = {
value.value = it
}, label = {
Text(
text = label, textAlign = TextAlign.Start,
color = Color.DarkGray,
style = NoteTheme.typography.subtitle
)
},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White),
keyboardActions = KeyboardActions(onNext =
{ focusManager.moveFocus((FocusDirection.Next)) })
)
}
}
#Composable
fun DatePick(mDate:MutableState<String>) {
val mContext = LocalContext.current
val mYear: Int
val mMonth: Int
val mDay: Int
val mCalendar = Calendar.getInstance()
mYear = mCalendar.get(Calendar.YEAR)
mMonth = mCalendar.get(Calendar.MONTH)
mDay = mCalendar.get(Calendar.DAY_OF_MONTH)
mCalendar.time = Date()
val mDatePickerDialog = DatePickerDialog(
mContext,
{ _: DatePicker, mYear: Int, mMonth: Int, mDayOfMonth: Int ->
mDate.value = "$mDayOfMonth/${mMonth + 1}/$mYear"
}, mYear, mMonth, mDay
)
mDatePickerDialog.datePicker.minDate=Date().time
Log.d("datepicker","dateset:${mDate.value}")
Button(modifier = Modifier
.padding(7.dp)
.fillMaxWidth(0.5f), onClick = {
mDatePickerDialog.show()
}, colors = ButtonDefaults.buttonColors(backgroundColor = Color(0XFF227093))) {
Text(text = "Seleccionar fecha",
color = Color.White, style = NoteTheme.typography.subtitle)
}
Text(text = "${mDate.value}",style = NoteTheme.typography.caption, fontSize = 22.sp, color = Color(0XFF227093))
}
#Composable
fun TimePick(mCalendar:Calendar) {
val mContext = LocalContext.current
val mHour = mCalendar[Calendar.HOUR_OF_DAY]
val mMinute = mCalendar[Calendar.MINUTE]
val mTime = remember { mutableStateOf("") }
val mTimePickerDialog = TimePickerDialog(
mContext,
{_, mHour : Int, mMinute: Int ->
mTime.value = "$mHour:$mMinute"
}, mHour, mMinute, false
)
Log.d("tmepicker","time:${mCalendar.timeInMillis}")
Button(modifier = Modifier
.padding(7.dp)
.fillMaxWidth(0.5f), onClick = {
mTimePickerDialog.show()
}, colors = ButtonDefaults.buttonColors(backgroundColor = Color(0XFF2c2c54))) {
Text(text = "Seleccionar hora",
color = Color.White, style = NoteTheme.typography.subtitle)
}
Text(text = "${mTime.value}",style = NoteTheme.typography.caption, fontSize = 22.sp, color = Color(0XFF2c2c54))
}
The problem I have is that I want the alarm to run at the scheduled time of the DatePicker ad TimePicker but it is running immediately !!
I don't know if this has something to do with the context, but how can I make the alarm be "scheduled" at the time and date of the DatePicker and Timepicker instead of firing at once
Well this was because when you get the calendar month you have to add one for some reason, so that was what caused an inaccurate time when setting the alarm. Final code:
class MainActivity : ComponentActivity() {
#RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val context= LocalContext.current
AlarmTestTheme {
mainPage(context)
}
}
}
}
#RequiresApi(Build.VERSION_CODES.M)
#Composable
fun mainPage(context: Context) {
val scope= rememberCoroutineScope()
val calendar=Calendar.getInstance().apply {
timeInMillis=System.currentTimeMillis()
}
val title = remember {
mutableStateOf("")
}
val description = remember {
mutableStateOf("")
}
val year= remember {
mutableStateOf(calendar.get(Calendar.YEAR))
}
val month= remember {
mutableStateOf(calendar.get(Calendar.MONTH)+1)
}
val day= remember {
mutableStateOf(calendar.get(Calendar.DAY_OF_MONTH))
}
val hour= remember {
mutableStateOf(calendar.get(Calendar.HOUR_OF_DAY))
}
val minutes= remember {
mutableStateOf(calendar.get(Calendar.MINUTE))
}
val iscompleted = remember {
mutableStateOf(false)
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(top = 30.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Agregar Tarea"
)
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
DatePick(context = context, year = year, month = month, day = day)
}
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
TimePick(context = context, hours = hour, minutes = minutes)
}
Button(
onClick = {
Log.d("tmepickerreceived","date and time :${year.value}/${month.value}/${day.value} : ${hour.value}:${minutes.value}")
calendar.apply {
set(Calendar.YEAR,year.value)
set(Calendar.MONTH,(month.value-1))
set(Calendar.DAY_OF_MONTH,day.value)
set(Calendar.HOUR_OF_DAY,hour.value)
set(Calendar.MINUTE,minutes.value)
}
val alarmMgr= context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val shuff=(1..99999).shuffled().first()
val intent= Intent(context,AlarmReceiver::class.java)
intent.action="alarma"
intent.putExtra("ALARMA_STRING","alarm number: $shuff")
val pending=
PendingIntent.getBroadcast(context,shuff,intent, PendingIntent.FLAG_IMMUTABLE)
Log.e("time final","compare ${System.currentTimeMillis()}" +
" mytime: ${calendar.timeInMillis} " +
"hour: ${calendar.get(Calendar.HOUR_OF_DAY)} " +
"minute: ${calendar.get(Calendar.MINUTE)} " +
"year: ${calendar.get(Calendar.YEAR)} " +
"month: ${calendar.get(Calendar.MONTH)+1} " +
"day: ${calendar.get(Calendar.DAY_OF_MONTH)}")
alarmMgr?.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,calendar.timeInMillis,pending)
Toast.makeText(context, "Alarm created", Toast.LENGTH_LONG)
.show()
},
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Blue)
) {
Text(text = "Guardar", color = Color.White)
}
}
}
#Composable
fun DatePick(context: Context, year: MutableState<Int>,month: MutableState<Int>,day: MutableState<Int>) {
val mDatePickerDialog = DatePickerDialog(
context,{d,mYear,mMonth,mDay ->
year.value=mYear
month.value=mMonth+1
day.value=mDay
}, year.value, month.value, day.value
)
mDatePickerDialog.datePicker.minDate=System.currentTimeMillis() - 1000
Log.d("datepicker","${day.value}/${month.value}/${year.value}")
Button(modifier = Modifier
.padding(7.dp)
.fillMaxWidth(0.5f), onClick = {
mDatePickerDialog.show()
}, colors = ButtonDefaults.buttonColors(backgroundColor = Color(0XFF227093))) {
Text(text = "Seleccionar fecha",
color = Color.White)
}
Text(text = "${day.value}/${month.value}/${year.value}", fontSize = 22.sp, color = Color(0XFF227093))
}
#Composable
fun TimePick(context: Context,hours:MutableState<Int>,minutes:MutableState<Int>) {
val mTimePickerDialog = TimePickerDialog(
context,
{_, mHour : Int, mMinute: Int ->
hours.value=mHour
minutes.value=mMinute
}, hours.value, minutes.value, false
)
Log.d("tmepicker","hour selected :${hours.value}: munute selected: ${minutes.value}")
Button(modifier = Modifier
.padding(7.dp)
.fillMaxWidth(0.5f), onClick = {
mTimePickerDialog.show()
}, colors = ButtonDefaults.buttonColors(backgroundColor = Color(0XFF2c2c54))) {
Text(text = "Seleccionar hora",
color = Color.White)
}
Text(text = "${hours.value}:${minutes.value}", fontSize = 22.sp, color = Color(0XFF2c2c54))
}
I want to develop a float draggable Button using jetpack compose,
also I need to know when user drag interaction starts and when it ends.
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
content = {
val interactionSource = remember { MutableInteractionSource() }
val interactions = remember { mutableStateListOf<Interaction>() }
LaunchedEffect(interactionSource) {
interactionSource.interactions.collect { interaction ->
Log.i("dragInteraction", "-> $interaction")
}
}
var offsetX by remember { mutableStateOf(0f) }
var offsetY by remember { mutableStateOf(0f) }
Surface(
modifier = Modifier
.offset {
IntOffset(
x = offsetX.roundToInt(),
y = offsetY.roundToInt()
)
}
.size(60.dp)
.pointerInput(Unit) {
detectDragGestures { change, dragAmount ->
offsetX += dragAmount.x
offsetY += dragAmount.y
change.consume()
}
},
interactionSource = interactionSource,
onClick = {
},
content = {
},
color = Purple500
)
}
)
in this code my Surface moves currectly but I can't get DragInteraction.Start and
DragInteraction.Stop when I'm dragging it!
all I get is
androidx.compose.foundation.interaction.PressInteraction$Press#c38442d androidx.compose.foundation.interaction.PressInteraction$Cancel#e6d1ef3
any suggestion how can I detect drag state ?
detectDragGestures doesn't emit DragInteraction by default. You should either emit DragInteraction.Start, DragInteraction.Stop, DragInteraction.Cancel
as
#OptIn(ExperimentalMaterial3Api::class)
#Composable
private fun DragInteractionSample() {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
content = {
val interactionSource = remember { MutableInteractionSource() }
val interactions = remember { mutableStateListOf<Interaction>() }
var text by remember { mutableStateOf("") }
LaunchedEffect(interactionSource) {
interactionSource.interactions.collect { interaction ->
when (interaction) {
is DragInteraction.Start -> {
text = "Drag Start"
}
is DragInteraction.Stop -> {
text = "Drag Stop"
}
is DragInteraction.Cancel -> {
text = "Drag Cancel"
}
}
}
}
val coroutineScope = rememberCoroutineScope()
var offsetX by remember { mutableStateOf(0f) }
var offsetY by remember { mutableStateOf(0f) }
val modifier = Modifier
.offset {
IntOffset(
x = offsetX.roundToInt(),
y = offsetY.roundToInt()
)
}
.size(60.dp)
.pointerInput(Unit) {
var interaction: DragInteraction.Start? = null
detectDragGestures(
onDragStart = {
coroutineScope.launch {
interaction = DragInteraction.Start()
interaction?.run {
interactionSource.emit(this)
}
}
},
onDrag = { change: PointerInputChange, dragAmount: Offset ->
offsetX += dragAmount.x
offsetY += dragAmount.y
},
onDragCancel = {
coroutineScope.launch {
interaction?.run {
interactionSource.emit(DragInteraction.Cancel(this))
}
}
},
onDragEnd = {
coroutineScope.launch {
interaction?.run {
interactionSource.emit(DragInteraction.Stop(this))
}
}
}
)
}
Surface(
modifier = modifier,
interactionSource = interactionSource,
onClick = {},
content = {},
color = MaterialTheme.colorScheme.primary
)
Text(text = text)
}
)
}
Result
or simply create an enum class and set it to a MutableState on each gesture function
enum class DragState {
Idle, DragStart, Drag, DragEnd, DragCancel
}
var dragState by remember{mutableStateOf(Idle}
And change this state on every drag callback.
Perhaps this is the normal behaviour, but i wish it was different. I had tried to google the solution, but did not find anything suitable (or merely missed it).
Sample code (for simplicity i hold mutable states right here, not using ViewModel):
#Composable
fun Greeting() {
Scaffold(topBar = {
TopAppBar(title = { Text(text = "Some title") })
}) {
val focusManager = LocalFocusManager.current
LazyColumn(
contentPadding = PaddingValues(all = 16.dp),
verticalArrangement = Arrangement.spacedBy(space = 16.dp)
) {
items(count = 20) { index ->
val (value, onValueChange) = rememberSaveable { mutableStateOf("Some value $index") }
TextField(
value = value,
onValueChange = onValueChange,
modifier = Modifier.fillMaxWidth(),
label = { Text(text = "Some label $index") },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(onNext = {
if (!focusManager.moveFocus(FocusDirection.Down))
focusManager.clearFocus()
}),
singleLine = true
)
}
}
}
}
Compose version 1.0.5
You could try just hiding the keyboard whenever scrolling occurs. This is okay as long as you don't have a large set of items. But since you're using TextFields, it isn't likely that you'll have such a large number. This sample illustrates hiding the keyboard when scrolling occurs:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startActivity(intent)
setContent {
Greeting(this)
}
}
}
#Composable
fun Greeting(activity: Activity) {
Scaffold(topBar = {
TopAppBar(title = { Text(text = "Some title") })
}) {
val lazyListState = rememberLazyListState()
val ctx = LocalContext.current
LaunchedEffect(lazyListState.firstVisibleItemIndex) {
val inputMethodManager = ctx.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(activity.window?.decorView?.windowToken, 0)
}
LazyColumn(
state = lazyListState,
contentPadding = PaddingValues(all = 16.dp),
verticalArrangement = Arrangement.spacedBy(space = 16.dp)
) {
items(
count = 20,
key = { index ->
// Return a stable + unique key for the item
index
}
) { index ->
val (value, onValueChange) = rememberSaveable { mutableStateOf("Some value $index") }
TextField(
value = value,
onValueChange = onValueChange,
modifier = Modifier
.fillMaxWidth(),
label = { Text(text = "Some label $index") },
singleLine = true
)
}
}
}
}