How can I select only ONE checkbox (jetpack compose/kotlin)? - kotlin

I have a list of options and each of them has a checkbox. I want the user to be able to select only one.
Chechkbox:
#Composable
fun CheckboxResource(isSelected: Boolean): Painter {
return if (isSelected) {
painterResource(id = R.drawable.check_on)
} else {
painterResource(id = R.drawable.check_off)
}
}
And the Composable:
fun SelectOptionsCheckbox(
isSelectedOption: Boolean,
onSelectOption: (Boolean) -> Unit
) {
val selectedOption = remember {
mutableStateOf(isSelectedOption)
}
Row {
Text()
Icon(
painter = CheckboxResource(isSelected = selectedOption.value),
contentDescription = "Checkbox",
modifier = Modifier
.clickable {
selectedOption.value = selectedOption.value.not()
onSelectOption(selectedOption.value)
},
)}
In this way the checkbox works but I can select all the options

I updated to imageVector because i don't have drawables you can use yours
#Composable
fun CheckboxResource(isSelected: Boolean): ImageVector {
return if (isSelected) {
Icons.Default.Check
} else {
Icons.Default.Cancel
}
}
#Composable
fun SelectOptionsCheckout(
text: String,
isSelectedOption: Boolean,
onSelectOption: (String) -> Unit
) {
Row {
Text(text)
Icon(
imageVector = CheckboxResource(isSelected = isSelectedOption),
contentDescription = "Checkbox",
modifier = Modifier
.clickable {
onSelectOption(text)
},
)
}
}
Checkbox group
#Composable
private fun CheckBoxGroup() {
val radioOptions = listOf("OptionA", "OptionB", "OptionC")
val (selectedOption: String, onOptionSelected: (String) -> Unit) = remember {
mutableStateOf(
radioOptions[0]
)
}
Column(Modifier.selectableGroup()) {
radioOptions.forEach { text ->
SelectOptionsCheckout(
text = text,
isSelectedOption = selectedOption == text,
onSelectOption = onOptionSelected
)
}
}
}
Checkbox Group with no initial selected item
Change state from Int to String and pass index of checkBox and return these on click and compare them previous one. If it's equal to previous one set new one as -1
#Composable
fun SelectOptionsCheckout(
index: Int,
text: String,
isSelectedOption: Boolean,
onSelectOption: (Int) -> Unit
) {
Row() {
Text(text)
Icon(
imageVector = CheckboxResource(isSelected = isSelectedOption),
contentDescription = "Checkbox",
modifier = Modifier
.clickable {
onSelectOption(index)
}
)
}
}
#Composable
private fun CheckBoxGroup() {
val radioOptions = listOf("OptionA", "OptionB", "OptionC")
val (selectedOption: Int, onOptionSelected: (Int) -> Unit) = remember {
mutableStateOf(
-1
)
}
Column(Modifier.selectableGroup()) {
radioOptions.forEachIndexed { index, text ->
SelectOptionsCheckout(
index = index,
text = text,
isSelectedOption = selectedOption == index,
onSelectOption = {
if (it == selectedOption) {
onOptionSelected(-1)
} else {
onOptionSelected(it)
}
}
)
}
}
}

Related

Jetpack compose scrollable table

I need to have a component that scrolls vertically and horizontally.
Here is what I did so far:
#Composable
fun Screen() {
val scope = rememberCoroutineScope()
val scrollOffset = remember {
mutableStateOf(value = 0F)
}
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(space = 16.dp)
) {
items(10) {
SimpleRow(
onScrollOffsetChange = {
scrollOffset.value = it
}
)
}
}
}
#Composable
private fun SimpleRow(
onScrollOffsetChange: (Float) -> Unit,
) {
val lazyRowState = rememberLazyListState()
LazyRow(
modifier = Modifier.fillMaxWidth(),
state = lazyRowState
) {
item {
Text(
text = "firstText"
)
}
for (i in 1..30) {
item {
Text(text = "qwerty")
}
}
}
}
When anyone of these SimpleRow is scrolled I want to scroll all of the SimpleRows together.
And I do not know how many SimpleRows I have because they came from our server.
Is there any kind of composable or trick that can do it for me?
You can use multiple rows inside a column that scrolls horizontally and vertically, like this:
#Composable
fun MainContent() {
val scrollStateHorizontal = rememberScrollState()
val scrollStateVertical = rememberScrollState()
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(state = scrollStateVertical)
.horizontalScroll(state = scrollStateHorizontal)
) {
repeat(40){ c ->
Row {
repeat(40){ r ->
Item("col: $c | row: $r")
}
}
}
}
}
#Composable
fun Item(text: String) {
Text(
modifier = Modifier
.padding(5.dp)
.background(Color.Gray),
text = text,
color = Color.White
)
}

Navigating to a new page in Kotlin Compose

I need help figuring out how when I click on the card it takes me to a whole new page that says "Success". Right now when I click it says "Success" but right above the card.
#OptIn(ExperimentalFoundationApi::class)
#Composable
fun AlbumList(title: String, url: String){
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "MainScreen") {
composable("MainScreen") {MainActivity() }
composable("album") { testSwitch(title, url) }
}
Card(
shape = RoundedCornerShape(5.dp),
elevation = 16.dp,
modifier = Modifier
.combinedClickable(
onClick = {
navController.navigate("album"){
popUpTo("album") { inclusive = true }
}
}
)
.padding(start = 16.dp, end = 16.dp, top = 5.dp, bottom = 5.dp),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Column(Modifier.padding(8.dp)) {
Text(
text = title,
style = MaterialTheme.typography.h4,
color = MaterialTheme.colors.onSurface,
)
Text(
text = url,
style = MaterialTheme.typography.body2,
)
}
}
}
}
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun testSwitch(title: String, url: String) {
Text("Success")
}
enter image description here
As defined in the documentation:
Define your NavHost and the respective routes...
NavHost(navController = navController, startDestination = "profile") {
composable("profile") { ProfileScreen(navController) }
composable("friendslist") { FriendsListScreen(navController) }
}
If you need to call the FriendsListScreen from ProfileScreen:
#Composable
fun ProfileScreen(navController: NavController) {
/*...*/
Button(onClick = { navController.navigate("friendslist") }) {
Text(text = "Navigate next")
}
/*...*/
}

Why textfield not catch text inside and don't add to database

When I press button with onSendClicked is not adding text from textfield. I don't know where not catching text. I guess is somewhere mistake with viewmodel, becouse viewmodel don't get new value.
fun AddBar(
onSendClicked: () -> Unit
){
Row(Modifier.padding(5.dp)) {
var title by remember {
mutableStateOf("")
}
TextField(
value = title,
onValueChange = { title = it }
)
IconButton(onClick = {
onSendClicked()})
{
Icon(imageVector = Icons.Filled.ArrowForward, contentDescription = "Send Icon")
}
}
}
#Composable
fun MainScreen(
basketViewModel: BasketViewModel,
){
AddBar(onSendClicked = { basketViewModel.addToBasket() })
}
And viewModel
val id: MutableState<Int> = mutableStateOf(0)
val title: MutableState<String> = mutableStateOf("")
fun addToBasket(){
viewModelScope.launch(Dispatchers.IO) {
val basket = Basket(
title = title.value,
isChecked = false
)
repository.addToBasket(basket = basket)
}
}
Help....
You are never using the title state of the ViewModel. You are only updating the local title. For this to you have to stop using local title and just replace it with the title from viewModel. Something like that:
fun AddBar(
title: MutableState<String>,
onSendClicked: () -> Unit
){
Row(Modifier.padding(5.dp)) {
TextField(
value = title.value,
onValueChange = { title.value = it }
)
IconButton(onClick = {
onSendClicked()})
{
Icon(imageVector = Icons.Filled.ArrowForward, contentDescription = "Send Icon")
}
}
}
#Composable
fun MainScreen(
basketViewModel: BasketViewModel,
){
AddBar(
title = basketViewModel.title,
onSendClicked = { basketViewModel.addToBasket() }
)
}
You define the title both in view model and your main screen. Use the one in your view model.
fun AddBar(
title: String,
onValueChange: (String) -> Unit,
onSendClicked: () -> Unit
){
Row(Modifier.padding(5.dp)) {
TextField(
value = title,
onValueChange = { onValueChange(it) }
)
IconButton(
onClick = { onSendClicked() }
) {
Icon(
imageVector = Icons.Filled.ArrowForward,
contentDescription = "Send Icon"
)
}
}
}
#Composable
fun MainScreen(
basketViewModel: BasketViewModel,
){
AddBar(
title = basketViewModel.title,
onValueChange = { basketViewModel.changeTitle(it) }
onSendClicked = { basketViewModel.addToBasket() }
)
}
class BasketViewModel : ViewModel() {
var title by mutableStateOf("")
private set
fun changeTitle(value: String) {
title = value
}
fun addToBasket(){
viewModelScope.launch(Dispatchers.IO) {
val basket = Basket(
title = title.value,
isChecked = false
)
repository.addToBasket(basket = basket)
}
}
}

Can I use $name instead of ${name}?

The Code A is from the sample project.
I think I can use ${name} instead of $name and $name instead of ${name} just like Code B, is it right?
Code A
#Composable
fun RallyNavHost(
navController: NavHostController,
modifier: Modifier = Modifier
) {
NavHost(
navController = navController,
startDestination = Overview.name,
modifier = modifier
) {
composable(Overview.name) {
OverviewBody(
onClickSeeAllAccounts = { navController.navigate(Accounts.name) },
onClickSeeAllBills = { navController.navigate(Bills.name) },
onAccountClick = { name ->
navController.navigate("${Accounts.name}/$name")
},
)
}
composable(Accounts.name) {
AccountsBody(accounts = UserData.accounts) { name ->
navController.navigate("Accounts/${name}")
}
}
Code B
#Composable
fun RallyNavHost(
navController: NavHostController,
modifier: Modifier = Modifier
) {
NavHost(
navController = navController,
startDestination = Overview.name,
modifier = modifier
) {
composable(Overview.name) {
OverviewBody(
onClickSeeAllAccounts = { navController.navigate(Accounts.name) },
onClickSeeAllBills = { navController.navigate(Bills.name) },
onAccountClick = { name ->
navController.navigate("${Accounts.name}/${name}") //I changed
},
)
}
composable(Accounts.name) {
AccountsBody(accounts = UserData.accounts) { name ->
navController.navigate("Accounts/$name") //I changed
}
}
yes you can
also read this article below:
kotlinlang.org/docs/basic-types.html#string-templates

How can select one out of 4 composabel in compose?

Suppose I have four Button or Composable in a row, I want to select one and deselect the other, not like the radio button but behave exactly like that.
#Composable
fun ButtonSet(modifier: Modifier = Modifier) {
Row(
modifier = modifier
.padding(start = 60.dp, end = 60.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly,
) {
val button1 = remember { mutableStateOf(false) }
val button2 = remember { mutableStateOf(false) }
val button3 = remember { mutableStateOf(false) }
val button4 = remember { mutableStateOf(false) }
val buttonList = listOf<Any>(button1, button2, button3, button4)
val activeButton = remember {
mutableStateOf(true)
}
if (button1.value || button2.value || button3.value || button4.value) {
activeButton.value = false
} else if (button1.value) {
button1.value = !button1.value
} else if (button2.value) {
button2.value = !button2.value
} else if (button3.value) {
button3.value = !button3.value
} else if (button4.value) {
button4.value = !button4.value
}
GoalkeeperButton(button1.value)
GoalkeeperButton(button2.value)
GoalkeeperButton(button3.value)
GoalkeeperButton(button4.value)
}
}
#Composable
fun GoalkeeperButton(
active: Boolean = false,
#SuppressLint("ModifierParameter") modifier: Modifier = Modifier,
) {
val buttonState = remember {
mutableStateOf(active)
}
if (!buttonState.value) {
ButtonUnActive() {}
} else {
ButtonActive()
}
RadioButton(selected =, onClick = { /*TODO*/ })
}
#Composable
fun ButtonActive(active: Boolean = true) {
val button = painterResource(id = R.drawable.bttn_pressed)
val gloves = painterResource(id = R.drawable.blue_hands)
val buttonState = remember {
mutableStateOf(active)
}
Column(
modifier = Modifier
.wrapContentWidth()
.height(130.dp)
) {
Image(
painter = gloves,
contentDescription = null,
contentScale = ContentScale.FillBounds,
modifier = Modifier
.size(width = 135.dp, height = 70.dp)
)
Image(
painter = button,
contentDescription = null,
contentScale = ContentScale.FillBounds,
modifier = Modifier
.clickable(onClick = {})
.size(width = 107.dp, height = 65.dp)
)
}
}
#Composable
fun ButtonUnActive(
state: Boolean = false,
onclick: () -> Unit,
) {
val button = painterResource(id = R.drawable.bttn)
var unActiveState by remember {
mutableStateOf(state)
}
Image(
painter = button,
contentDescription = null,
contentScale = ContentScale.FillBounds,
modifier = Modifier
.clickable(
onClick = {
onclick()
},
)
.size(width = 107.dp, height = 65.dp)
)
}
This is what I tried, but I know it's very bad code, please suggest a better and best way.