Issue
I try show AlertDialog when press a button. For AlertDialog i have a composable function - showDialog. It is clear that this function calls a dialog. I have another composable function which displays some window with text and buttons. When the button is clicked, I want to call a function that stores the AlertDialog.
AlertDialog body:
fun monthDialog() {
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")
}
}
}
)
}
first I tried the simplest solution that came to my mind:
IconButton(onClick = monthDialog())
and got error (@Composable invocations can only happen from the context of a @Composable function)
after i tried a normal trigger using mutablestateof and following condition:
val showDialog = remember { mutableStateOf(false)}
if(showDialog.value == true) monthDialog()
and put some like this into onClick event:
monthHeader(onClick = {showDialog.value = !showDialog.value})
and this is work....but ugly and badly. for a first time this is worf fine. but after the first click, I need to click two more times to trigger that event again.
button code snippet:
fun Calendar (){
//...
val showDialog = remember { mutableStateOf(false)}
if(showDialog.value == true) monthDialog()
Card(
){
Column(){
IconButton(onClick ={
monthDialog() // error here
//showDialog.value = !showDialog.value
}
}
}
after few hours for searching in google i try my own solution
val clicked = remember { mutableStateOf(false)}
val showDialog = remember { mutableStateOf(false)}
if(showDialog.value) monthDialog()
else if(clicked.value) monthDialog()
Card(){
Column(){
monthHeader(onClick = {
clicked.value = showDialog.value
showDialog.value = !clicked.value
})
}
}
but in my opinion this is crutch/kludge
Solution
Leaving a better solution here (imho):
Hoist the state of your dialog.
@Composable
fun MonthDialog(onClose: () -> Unit) {
AlertDialog(
onDismissRequest = onClose,
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 = onClose
) {
Text("Dismiss")
}
}
}
)
Noticed that I removed the state from this component and make it stateless. This component will just notify when the dialog is closed.
Now you can call the dialog like this:
var showDialog by remember { mutableStateOf(false) }
if (showDialog) {
MonthDialog(onClose = { showDialog = false })
}
Card {
MonthHeader( // use upper case for naming your composables
onClick = {
showDialog = true
}
)
}
Answered By - nglauber
Answer Checked By - Marilyn (JavaFixing Volunteer)