Custom Alert Dialog in Android Using Jetpack Compose

Jetpack Compose Custom Dialog

This is a custom alert dialog made with Android Jetpack Compose. It is a delete confirmation pop-up box. It has two buttons positive and negative. You can download the source code below.

The Android custom alert dialog uses the Dialog, Surface, Icon, Text, and Box APIs. It doesn’t follow material guidelines. The Box layout is used to create the confirm and cancel buttons. It also contains a message saying “Are you sure you want to delete this item from the list?”. I have set the icon before the title.

If you want to display the dialog tap on the button. You can cancel it by taping on either of the positive or negative buttons. I added the Surface inside the Dialog to get the functionalities of the Dialog API like background overlay, and animation.

Final output:

If the video isn’t working, watch it on the YouTube.

Helpful links to understand the code:

You can download the delete icon from feature icons. Open the site and search for “trash 2”. You will get the svg icon. But, you need to convert the icon to XML format. Here is the tutorial on how to convert SVG icons to XML in Android Studio.

Here are the Gradle files used in the project.

Here is the source code:

MainActivity.kt:

import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog


/*
You can use the following code for commercial purposes with some restrictions.
Read the full license here: https://semicolonspace.com/semicolonspace-license/
For more designs with source code,
visit: https://semicolonspace.com/jetpack-compose-samples/
 */
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            BlogPostsTheme(darkTheme = false) {
                Column(
                    modifier = Modifier
                        .fillMaxSize()
                        .background(color = MaterialTheme.colors.background),
                    verticalArrangement = Arrangement.Center,
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {

                    var openDialog by remember {
                        mutableStateOf(false) // Initially dialog is closed
                    }

                    ButtonClick(buttonText = "Open Dialog") {
                        openDialog = true
                    }

                    if (openDialog) {
                        DialogBoxDeleteItem {
                            openDialog = false
                        }
                    }
                }
            }
        }
    }
}

@Composable
fun ButtonClick(
    buttonText: String,
    onButtonClick: () -> Unit
) {
    Button(
        shape = RoundedCornerShape(5.dp),
        colors = ButtonDefaults.buttonColors(backgroundColor = MaterialTheme.colors.primary),
        onClick = {
            onButtonClick()
        }) {
        Text(
            text = buttonText,
            fontSize = 16.sp,
            color = Color.White
        )
    }
}

// For Roboto font: https://fonts.google.com/specimen/Roboto?query=roboto
@Composable
fun DialogBoxDeleteItem(
    cornerRadius: Dp = 12.dp,
    deleteButtonColor: Color = Color(0xFFFF0000),
    cancelButtonColor: Color = Color(0xFF35898F),
    titleTextStyle: TextStyle = TextStyle(
        color = Color.Black.copy(alpha = 0.87f),
        fontFamily = FontFamily(Font(R.font.roboto_bold, FontWeight.Bold)),
        fontSize = 20.sp
    ),
    messageTextStyle: TextStyle = TextStyle(
        color = Color.Black.copy(alpha = 0.95f),
        fontFamily = FontFamily(Font(R.font.roboto_regular, FontWeight.Normal)),
        fontSize = 16.sp,
        lineHeight = 22.sp
    ),
    buttonTextStyle: TextStyle = TextStyle(
        fontFamily = FontFamily(Font(R.font.roboto_medium, FontWeight.Medium)),
        fontSize = 16.sp
    ),
    onDismiss: () -> Unit
) {

    val context = LocalContext.current.applicationContext

    // This helps to disable the ripple effect
    val interactionSource = remember {
        MutableInteractionSource()
    }

    val buttonCorner = 6.dp

    Dialog(
        onDismissRequest = {
            onDismiss()
        }
    ) {

        Surface(
            modifier = Modifier
                .fillMaxWidth()
                .wrapContentHeight(),
            shape = RoundedCornerShape(size = cornerRadius)
        ) {

            Column(modifier = Modifier.padding(all = 16.dp)) {

                Row(
                    modifier = Modifier
                        .fillMaxWidth(),
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.spacedBy(
                        space = 6.dp,
                        alignment = Alignment.Start
                    )
                ) {

                    // For icon, visit feathericons.com
                    // Icon name: trash-2
                    Icon(
                        modifier = Modifier.size(26.dp),
                        painter = painterResource(id = R.drawable.trash_2),
                        contentDescription = "Delete Icon",
                        tint = deleteButtonColor
                    )

                    Text(
                        text = "Delete Item?",
                        style = titleTextStyle
                    )

                }

                Text(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(top = 16.dp, bottom = 20.dp),
                    text = "Are you sure you want to delete this item from the list?",
                    style = messageTextStyle
                )

                Row(
                    modifier = Modifier.fillMaxWidth(),
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.spacedBy(
                        space = 10.dp,
                        alignment = Alignment.End
                    )
                ) {

                    // Cancel button
                    Box(
                        modifier = Modifier
                            .clickable(
                                // This is to disable the ripple effect
                                indication = null,
                                interactionSource = interactionSource
                            ) {
                                Toast
                                    .makeText(context, "Cancel", Toast.LENGTH_SHORT)
                                    .show()
                                onDismiss()
                            }
                            .border(
                                width = 1.dp,
                                color = cancelButtonColor,
                                shape = RoundedCornerShape(buttonCorner)
                            )
                            .padding(top = 6.dp, bottom = 8.dp, start = 24.dp, end = 24.dp),
                    ) {
                        Text(
                            text = "Cancel",
                            style = buttonTextStyle,
                            color = cancelButtonColor
                        )
                    }

                    // Delete button
                    Box(
                        modifier = Modifier
                            .clickable(
                                // This is to disable the ripple effect
                                indication = null,
                                interactionSource = interactionSource
                            ) {
                                Toast
                                    .makeText(context, "Delete", Toast.LENGTH_SHORT)
                                    .show()
                                onDismiss()
                            }
                            .background(
                                color = deleteButtonColor,
                                shape = RoundedCornerShape(buttonCorner)
                            )
                            .padding(top = 6.dp, bottom = 8.dp, start = 24.dp, end = 24.dp),
                    ) {
                        Text(
                            text = "Delete",
                            style = buttonTextStyle,
                            color = Color.White
                        )
                    }

                }
            }

        }

    }
}

Related:

Leave a Comment