Compose AlertDialog not closing - android

I have a Dialog that I cant close when I press the button this is my code, I believe that is for the if cycle, but I need it to make it true under those circumstances, any idea that could help me here?
#Composable
fun PopupWindowDialog(
parentUiState: ParentHomeUiState,
) {
val openDialog = remember { mutableStateOf(false) }
var sliderPosition by remember { mutableStateOf(0f) }
if (!parentUiState.showInAppFeedback){
openDialog.value = true
}
val recommend = sliderPosition.toInt()
Column(
) {
Box {
if (openDialog.value) {
Dialog(
onDismissRequest = { openDialog.value = false },
properties = DialogProperties(),
){
Box(
Modifier
.fillMaxWidth()
.fillMaxHeight()
//.padding(vertical = 70.dp, horizontal = 10.dp)
.padding(vertical = 70.dp )
.background(Color.White, RoundedCornerShape(10.dp))
//.border(1.dp, color = Color.Black, RoundedCornerShape(20.dp))
.border(1.dp, color = Color.White, RoundedCornerShape(20.dp))
) {
Button(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
onClick = {
openDialog.value = !openDialog.value
}
) {
Text(
text = "¡Contesta y gana +20 puntos!",
style = MaterialTheme.typography.subtitle2,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(3.dp))
}
}
}
}
}
}
}

I have a Dialog that I cant close when I press the button
Since you didn't show how your posted code (composable) is being called, ill assume the .showInAppFeedback is evaluating true.
Pay attention to these parts of your codes
val openDialog = remember { mutableStateOf(false) }
...
...
if (!parentUiState.showInAppFeedback) {
openDialog.value = true
}
, openDialog is a state and when it changes value it will re-compose your entire composable, so when the dialog is visible, and when you set it to false via button click, the entire composable will re-compose and re-evaluate your if condition, setting openDialog.value to true again, and dialog will never close.
I don't know your use-case but you can wrap it inside a LaunchedEffect
LaunchedEffect(parentUiState.showInAppFeedback) {
openDialog.value = true
}

Since parentUiState.showInAppFeedback is not updated, openDialog.value will always be set to true when the view recomposes. You should use parentUiState.showInAppFeedback in the initial remember block but not for future recomposition.
#Composable
fun PopupWindowDialog(parentUiState: ParentHomeUiState) {
val openDialog = remember { mutableStateOf(!parentUiState.showInAppFeedback) }
var sliderPosition by remember { mutableStateOf(0f) }
val recommend = sliderPosition.toInt()
Column {
Box {
if (openDialog.value) {
Dialog(
onDismissRequest = { openDialog.value = false },
properties = DialogProperties(),
) {
Box(
Modifier
.fillMaxWidth()
.fillMaxHeight()
//.padding(vertical = 70.dp, horizontal = 10.dp)
.padding(vertical = 70.dp)
.background(Color.White, RoundedCornerShape(10.dp))
//.border(1.dp, color = Color.Black, RoundedCornerShape(20.dp))
.border(1.dp, color = Color.White, RoundedCornerShape(20.dp))
) {
Button(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
onClick = {
openDialog.value = !openDialog.value
}
) {
Text(
text = "¡Contesta y gana +20 puntos!",
style = MaterialTheme.typography.subtitle2,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(3.dp)
)
}
}
}
}
}
}
}

Related

unable botton when slider moves jetpack compose

I have a simple slider that I need to disable the button at star and when user moves the slider it should be enabled, i can disable it at first but i cant to enable it back, for some reason it doesnt take the new variable value this is my code.
My thoughts its because the var is declared at first and the slider value is capsuled somehow?
but if that is the case how do i pass the value to the component?
#OptIn(ExperimentalComposeUiApi::class)
#Composable
fun PopupWindowDialogStudent(
onConfirm: (Int, String) -> Unit,
parentUiState: StudentHomeUiState,
) {
// val openDialog = remember { mutableStateOf(parentUiState.showInAppFeedback) }
val openDialog = remember { mutableStateOf(!parentUiState.showInAppFeedback) }
var sliderPosition by remember { mutableStateOf(5f) }
var enable by remember { mutableStateOf(false) }
val recommend = sliderPosition.toInt()
Column(
) {
Box {
if (openDialog.value) {
Dialog(
onDismissRequest = { openDialog.value = false },
properties = DialogProperties(),
)
{
Box(
Modifier
.fillMaxWidth()
.height(500.dp)
.background(Color.White, RoundedCornerShape(10.dp))
.border(1.dp, color = Color.White, RoundedCornerShape(20.dp))
) {
Column(
modifier = Modifier
.fillMaxSize()
.fillMaxHeight()
.padding(horizontal = 5.dp)
.verticalScroll(rememberScrollState())
,
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Column(horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 10.dp)) {
var completeValue by remember { mutableStateOf("") }
completeValue = sliderPosition.toString()
Column(horizontalAlignment = Alignment.CenterHorizontally){
Slider(
value = sliderPosition,
onValueChange = {
sliderPosition = it
var enable = true
},
valueRange = 0f..10f,
steps = 9,
onValueChangeFinished = {
completeValue
},
modifier = Modifier.width(180.dp),
colors = SliderDefaults.colors(
thumbColor = secondaryColor,
activeTrackColor = Color.Blue
)
)
}
}
Spacer(modifier = Modifier.height(10.dp))
var comment by remember { mutableStateOf("") }
val keyboardController = LocalSoftwareKeyboardController.current
TextField(
value = comment,
placeholder = { Text(text = "¿Tienes algún otro comentario?") },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = {keyboardController?.hide()}),
onValueChange = {
comment = it
}
)
Spacer(modifier = Modifier.height(10.dp))
if (enable){
Button(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
onClick = {
onConfirm(recommend,comment)
openDialog.value = !openDialog.value
}
) {
Text(
text = "¡Contesta y gana +20 puntos!",
style = MaterialTheme.typography.subtitle2,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(3.dp))
}
}else{
Button(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
onClick = {
}
) {
Text(
text = "¡Contesta y gana +20 puntos!",
style = MaterialTheme.typography.subtitle2,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(3.dp))
}
}
}
}
}
}
}
}
}
}
In the Slider you can use the onValueChangeFinished to change the enable value:
var enable by remember { mutableStateOf(false) }
Slider(
value = sliderPosition,
onValueChange = {
sliderPosition = it
enable = true
},
onValueChangeFinished = {
completeValue
enable = false
},
//...
)
Also you can avoid to use the if statement for the Button. Just use:
Button(
//...
enabled = enable,
onClick = {
openDialog.value = !openDialog.value
}
) {
//...
}

How can I fix my alert dialog in kotlin compose?

I' ve been making a dictionary app for a while and I added that users can create own dictionaries on my app. I show users dictionary on the screen and users can delete their dictionaries whatever they want. so I am trying to make alert dialog for this because I want users not to delete their dictionaries when they press the delete icon directly. An alert dialog will appear on the screen and there should be two buttons such as cancel and accept in that alert dialog. If the user presses accept, that is, if he wants to delete, I want the dictionary to be deleted.
However, the problem is that it is difficult to implement this in compose and in the codes I wrote because I encountered many problems for some reason, whereas it should have been easy. What I did in my codes is that if user clicks delete icon onDeleteClick works and showAlertDialog becomes true in onDeleteClick. When true, it goes inside the top if block and calls the alert dialog component. When the alert dialog compo is called, CustomDialogUI opens. I send two parameters to CustomDialogUI, one is a showAlertDialog mutablestate that controls the opening and closing of the alert dialog, and the second one is deleteDicState if the user says allow in the alert dialog that opens, deleteDicState becomes true and if deleteDicState is true, the deletion must occur.
Since deleteDicState is false the first time, it does not delete, but when the alert dialog opens for the second time and I say delete, it deletes it for some reason. How can i solve this problem help.
my code
#Composable
fun CreateYourOwnDictionaryScreen(
navController: NavController,
viewModel: CreateYourOwnDictionaryViewModel = hiltViewModel()
) {
val scaffoldState = rememberScaffoldState()
val state = viewModel.state.value
val scope = rememberCoroutineScope()
val context = LocalContext.current
val showAlertDialog = remember { mutableStateOf(false) }
val deleteDicState = remember { mutableStateOf(false) }
if(showAlertDialog.value){
Dialog(onDismissRequest = { showAlertDialog.value = false }) {
CustomDialogUI(openDialogCustom = showAlertDialog,deleteDicState)
}
}
Scaffold(
scaffoldState = scaffoldState,
topBar = {
TopAppBar(
backgroundColor = bar,
title = {
androidx.compose.material3.Text(
text = "your dictionaries",
modifier = Modifier.fillMaxWidth(),
color = Color.White,
fontSize = 22.sp
)
},
navigationIcon = {
IconButton(onClick = {
navController.navigate(Screen.MainScreen.route)
}) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Go Back"
)
}
}
)
},
floatingActionButtonPosition = FabPosition.Center,
floatingActionButton = {
FloatingActionButton(
onClick = { navController.navigate(Screen.CreateDicScreen.route) },
backgroundColor = bar,
) {
Icon(Icons.Filled.Add, "fab")
}
}
) {
Box(modifier = Modifier.background(MaterialTheme.colors.background)) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
items(state.dictionaries) { dictionary ->
CreateYourOwnDictionaryItem(
dictionary = dictionary,
modifier = Modifier
.fillMaxWidth()
.clickable {
navController.navigate(Screen.MyWordsScreen.passDicId(dictionary.uid))
},
onAddClick = {
navController.navigate(
Screen.MakeYourDictionaryScreen.passDicId(
dictionary.uid
)
)
},
onDeleteClick = {
if(deleteDicState.value){
viewModel.onEvent(
CreateYourOwnDictionaryEvents.DeleteDictionary(dictionary)
)
scope.launch {
val result = scaffoldState.snackbarHostState.showSnackbar(
message = "dictionary is deleted",
/*actionLabel = "Undo",*/
duration = SnackbarDuration.Short
)
}
}
},
onEditClick = {
navController.navigate(
Screen.UpdateOwnDictionaryScreen.passDicIdAndDicName(
dictionary.uid,
dictionary.creationTime,
)
)
}
)
}
}
}
}
}
}
#Composable
fun CustomDialogUI(
openDialogCustom: MutableState<Boolean>,
deleteDicState : MutableState<Boolean>
) {
Card(
//shape = MaterialTheme.shapes.medium,
shape = RoundedCornerShape(10.dp),
// modifier = modifier.size(280.dp, 240.dp)
modifier = Modifier.padding(10.dp, 5.dp, 10.dp, 10.dp),
elevation = 8.dp
) {
Column(
modifier = Modifier
.background(Color.White)
) {
//.......................................................................
Image(
painter = painterResource(id = R.drawable.ic_baseline_warning),
contentDescription = null, // decorative
/*contentScale = ContentScale.Fit,
colorFilter = ColorFilter.tint(
color = bar
),*/
modifier = Modifier
.padding(top = 35.dp)
.height(70.dp)
.fillMaxWidth(),
)
Column(modifier = Modifier.padding(16.dp)) {
androidx.compose.material3.Text(
text = "Warning !",
textAlign = TextAlign.Center,
modifier = Modifier
.padding(top = 5.dp)
.fillMaxWidth(),
style = MaterialTheme.typography.body2,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
androidx.compose.material3.Text(
text = "Are you sure that your previously created dictionary will be deleted?",
textAlign = TextAlign.Center,
modifier = Modifier
.padding(top = 10.dp, start = 25.dp, end = 25.dp)
.fillMaxWidth(),
)
}
//.......................................................................
Row(
Modifier
.fillMaxWidth()
.padding(top = 10.dp)
.background(bar),
horizontalArrangement = Arrangement.SpaceAround
) {
TextButton(onClick = {
openDialogCustom.value = false
}) {
Text(
"Not Now",
fontWeight = FontWeight.Bold,
color = Color.Black,
modifier = Modifier.padding(top = 5.dp, bottom = 5.dp)
)
}
TextButton(onClick = {
openDialogCustom.value = false
deleteDicState.value = true
}) {
Text(
"Allow",
fontWeight = FontWeight.ExtraBold,
color = Color.Black,
modifier = Modifier.padding(top = 5.dp, bottom = 5.dp)
)
}
}
}
}
}
I cannot call the CustomDialogUI in onDeleteClick . If I call it, it gives the following error #Composable invocations can only happen from the context of a #Composable function.
for example like this
CreateYourOwnDictionaryScreen
onDeleteClick = {
Dialog(onDismissRequest = { showAlertDialog.value = false }) {
CustomDialogUI(openDialogCustom = showAlertDialog,deleteDicState)
}
....
I cannot call like this.
So I call it outside of onDeleteClick. or directly in CustomDialogUI if the user presses the delete button, I cannot delete it there because I can't access viewmodel and dictionary there
for example like this
CustomDialogUI
TextButton(onClick = {
openDialogCustom.value = false
viewModel.onEvent(
CreateYourOwnDictionaryEvents.DeleteDictionary(dictionary)
)
scope.launch {
val result = scaffoldState.snackbarHostState.showSnackbar(
message = "dictionary is deleted",
/*actionLabel = "Undo",*/
duration = SnackbarDuration.Short
)
}
}) {
Text(
"Allow",
fontWeight = FontWeight.ExtraBold,
color = Color.Black,
modifier = Modifier.padding(top = 5.dp, bottom = 5.dp)
)
}
}
I cannot call like this.
Passing MutableStates as composable parameters is considered a bad practice, you should pass raw values and callbacks instead. In your case, you can implement it like this:
#Composable
fun CreateYourOwnDictionaryScreen() {
val showDeleteDialogForItem = remember { mutableStateOf<DictionaryItem?>(null) }
showDeleteDialogForItem.value?.let { itemToDelete ->
DeleteDialog(
onDeleteConfirm = {
viewModel.onEvent()
showDeleteDialogForItem.value = null
},
onCancel = { showDeleteDialogForItem.value = null },
)
}
...
items.forEach { item ->
CreateYourOwnDictionaryItem(
onDeleteClick = { showDeleteDialogForItem.value = item }
)
}
}
#Composable
fun DeleteDialog(
onDeleteConfirm: () -> Unit,
onCancel: () -> Unit,
) {
...
Button(onClick = onCancel) { Text("Cancel") }
Button(onClick = onDeleteConfirm) { Text("Delete") }
}

Autocomplete TextField move down other views in jetpack compose

I wrote an AutoCompletet TextField in Jetpack Compose. I used this view in a Row and between two others' views. The side views move down when autocomplete dropdown is opened. How can I fix this issue?
The Link of GIF file is end of page. in the GIF you can see side views of Autocomplete move to bottom when the suggest items is shows.
My AutoComplete codes:
#Composable
fun AutoCompleteTextField(
modifier: Modifier = Modifier,
list: List<Product>,
searchTerm: TextFieldValue,
updateSearchTerm: (TextFieldValue) -> Unit,
) {
var product by remember {
mutableStateOf("")
}
var heightTextFields by remember {
mutableStateOf(55.dp)
}
var textFieldSize by remember {
mutableStateOf(Size.Zero)
}
var expanded by remember {
mutableStateOf(false)
}
val interactionSource = remember {
MutableInteractionSource()
}
Column(
modifier = modifier
.width(200.dp)
// .padding(30.dp)
.clickable(
interactionSource = interactionSource,
indication = null,
onClick = {
expanded = false
}
)
) {
Column(modifier = modifier.fillMaxWidth()) {
Row(
modifier = modifier
.border(BorderStroke(width = 1.dp, color = Color.LightGray))
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
IconButton(onClick = {product = "" }) {
Icon(
imageVector = Icons.Filled.Add,
contentDescription = "Add",
modifier = modifier
.padding(4.dp)
.align(Alignment.CenterVertically)
)
}
TextField(
modifier = Modifier
.fillMaxWidth()
.height(heightTextFields)
.onGloballyPositioned { coordinates ->
textFieldSize = coordinates.size.toSize()
},
value = product,
onValueChange = {
product = it
expanded = true
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
cursorColor = Color.Black
),
textStyle = TextStyle(
fontFamily = shabnamFontFamily,
color = MaterialTheme.colorScheme.secondary,
fontWeight = FontWeight.Bold,
fontSize = 14.sp,
),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Done
),
singleLine = true,
)
}
AnimatedVisibility(visible = expanded) {
Card(
modifier = modifier
.padding(horizontal = 4.dp)
.width(textFieldSize.width.dp),
elevation = 10.dp,
shape = RoundedCornerShape(bottomStart = 4.dp, bottomEnd = 4.dp)
) {
LazyColumn(
modifier = Modifier.heightIn(max = 150.dp),
) {
if (product.isNotEmpty()) {
items(
list.filter {
it.productName.lowercase()
.contains(product.lowercase())
}
) { item ->
ProductItems(title = item.productName) { title ->
product = title
expanded = false
}
}
} else {
items(
items =
list
) { item ->
ProductItems(title = item.productName) { title ->
product = title
expanded = false
}
}
}
}
}
}
}
}
}
}
Screen GIF file

compile time error: #Composable invocations can only happen from the context of a #Composable function

I have a compile time error as shown in the title
(#Composable invocations can only happen from the context of a #Composable function),
I have one card in this code so I want to show An AlertDialog after clicking the card clickable,
I know that this problem is exist in the platform,
So how can i solve this problem?
#Composable
fun Screen_A_with_WithTopBar(navController: NavController) {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(text = "Top App Bar")
},
navigationIcon = {
IconButton(onClick = {
navController.navigate(Screen.Home.route)
}) {
Icon(Icons.Filled.Menu, "backIcon")
}
},
backgroundColor = MaterialTheme.colors.primary,
contentColor = Color.White,
elevation = 10.dp
)
}, content = {
Screen_A(navController)
}
)
}
#Composable
fun Screen_A(navController: NavController) {
val context = LocalContext.current
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
//.background(ScreenColor)
) {
Text(
text = "Dooro",
fontSize = 42.sp
)
Spacer(modifier = Modifier.height(33.dp))
Row(
verticalAlignment = Alignment.Top,
horizontalArrangement = Arrangement.SpaceAround,
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
) {
Card(
modifier = Modifier
.width(150.dp)
.height(80.dp)
.clickable {
Alert()
},
RoundedCornerShape(7.dp),
elevation = 7.dp
//.padding(40.dp)
) {
Text(
text = "None",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center
)
}
} // END Row
}
}
#Composable
fun Alert() {
val openDialog = remember { mutableStateOf(true) }
if (openDialog.value) {
AlertDialog(
onDismissRequest = {
openDialog.value = false
},
title = {
Text(text = "Title")
},
text = {
Text(
"This area typically contains the supportive text " +
"which presents the details regarding the Dialog's purpose."
)
},
buttons = {
Row(
modifier = Modifier.padding(all = 8.dp),
horizontalArrangement = Arrangement.Center
) {
Button(
modifier = Modifier.fillMaxWidth(),
onClick = { openDialog.value = false }
) {
Text("Dismiss")
}
}
}
)
}
}
Here you can use mutableStateOf to be able to show your Alert.
on Screen_A, you should add the following
var showAlert by mutableStateOf(false)
#Composable
private fun ShowAlert() {
if (showAlert)
Alert()
}
Finally in your Card, you will change the value of showAlert field to true.
Card(
modifier = Modifier
.width(150.dp)
.height(80.dp)
.clickable {
showAlert = true
},
RoundedCornerShape(7.dp),
elevation = 7.dp
//.padding(40.dp)
)

Requesting focus on a BasicTextField in Jetpack Compose

I have simple LazyColumn where each row contains a BasicTextField (the rows contain label names which can be changed). The row's state is set to "editMode" when it is clicked. Only then I want the BasicTextField to be editable, which makes the edit icons visible. However, I have to click two times on the row, but I want it to set focus on the TextField when the row is clicked for the first time. Using the focusRequest just does not work (nothing happens). Where could the problem be? Here is the code:
val focusRequester = FocusRequester()
val inputService = LocalTextInputService.current
Row(
modifier = modifier
.fillMaxWidth()
.height(48.dp)
.clickable(enabled = true) {
onLabelClick() // here I set the editMode = true
inputService?.showSoftwareKeyboard()
focusRequester.requestFocus()
}
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Box(
modifier = Modifier.weight(1f)
) {
BasicTextField(
modifier = Modifier
.fillMaxSize()
.focusRequester(focusRequester)
.focusable(enabled = true),
value = tmpLabelName,
onValueChange = {
tmpLabelName = it
},
enabled = editMode,
singleLine = true,
textStyle = MaterialTheme.typography.body1,
decorationBox = { innerTextField ->
Box(
contentAlignment = Alignment.CenterStart
) {
if (tmpLabelName.isEmpty()) {
// show hint if text is empty
Text(
text = stringResource(id = R.string.enter_label_name)
)
} else {
innerTextField()
}
}
}
)
}
AnimatedVisibility(
visible = editMode,
enter = slideInHorizontally(
initialOffsetX = { fullWidth ->
fullWidth / 2
}
),
exit = fadeOut()
) {
Row {
IconButton(onClick = {
onLabelSaveClick(
label.name, tmpLabelName
)
}) {
Icon(
imageVector = Icons.Filled.Check,
contentDescription = stringResource(id = R.string.save_label_changes)
)
}
IconButton(onClick = {
onLabelDeleteClick(label.name)
}) {
Icon(
imageVector = Icons.Filled.Delete,
contentDescription = stringResource(id = R.string.delete_label)
)
}
}
}
}

Categories

Resources