NewPartizal
NewPartizal

Reputation: 1136

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.

Upvotes: 0

Views: 983

Answers (1)

Jan B&#237;na
Jan B&#237;na

Reputation: 7318

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") }
}

Upvotes: 1

Related Questions