Dialog APIs in Material 3 Jetpack Compose (with Example)

Jetpack Compose Material 3 Dialog

In this article, we’ll learn how to implement the dialog APIs in Material 3 Jetpack Compose.

Prerequisites:

This article talks about the Material 3 APIs. For the Material 2 version, follow this link.

What is a Dialog?

A dialog is a type of modal window. When it is displayed, access to the rest of the screen is restricted until the user takes an action or dismisses the dialog.

Example:

Dialog Example

Let’s look at the dialog APIs in Material 3 Jetpack Compose.

First, create an empty Compose project and open the MainActivity.kt. Create a MyUI() composable and call it from the onCreate() method. We’ll write our code in it.

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.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.DialogProperties

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            YourProjectNameTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Column(
                        modifier = Modifier
                            .fillMaxSize(),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        MyUI()
                    }
                }
            }
        }
    }
}

@Composable
fun MyUI() {

}

In the Android Studio, start typing AlertDialog and select the one with the confirm and dismiss buttons.

Material 3 AlertDialog with Buttons

It takes multiple parameters:

@Composable
fun AlertDialog(
    onDismissRequest: () -> Unit,
    confirmButton: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    dismissButton: @Composable (() -> Unit)? = null,
    icon: @Composable (() -> Unit)? = null,
    title: @Composable (() -> Unit)? = null,
    text: @Composable (() -> Unit)? = null,
    shape: Shape = AlertDialogDefaults.shape,
    containerColor: Color = AlertDialogDefaults.containerColor,
    iconContentColor: Color = AlertDialogDefaults.iconContentColor,
    titleContentColor: Color = AlertDialogDefaults.titleContentColor,
    textContentColor: Color = AlertDialogDefaults.textContentColor,
    tonalElevation: Dp = AlertDialogDefaults.TonalElevation,
    properties: DialogProperties = DialogProperties()
)

onDismissRequest – It is called when the user tries to dismiss the Dialog by clicking outside or pressing the back button. This is not called when the dismissButton is clicked.

confirmButton – The confirm button on the dialog. The dialog does not set up any events for this button, so they need to be set up by us. We use TextButton() here.

modifier – The Modifier to be applied to this dialog.

dismissButton – The dismiss button on the dialog. It is similar to confirmButton. We use the TextButton().

icon – An optional icon that will appear above the title.

title – It specifies the purpose of the dialog. The title is not mandatory, because there may be sufficient information inside the text. It should be added using the Text() composable.

text – It conveys the purpose of the dialog. We use Text() composalbe to add the text.

shape – It defines the shape of this dialog’s container. We can use any shape API that Jetpack Compose provides.

containerColor – The background color of the dialog. Use Color.Transparent to have no color.

iconContentColor – The color of the icon.

titleContentColor – The color of the title.

textContentColor – The color of the text.

tonalElevation – Elevation of the dialog.

properties – It helps us to further customize the dialog. For example, we can make the dialog full screen using it.

Simple Alert Dialog Example:

@Composable
fun MyUI() {
    var openDialog by remember {
        mutableStateOf(false) // initially, don't show the dialog
    }

    val contextForToast = LocalContext.current.applicationContext

    if (openDialog) {
        AlertDialog(
            onDismissRequest = {
            // Dismiss the dialog when the user clicks outside the dialog or on the back
            // button. If you want to disable that functionality, simply leave it empty.
                openDialog = false
            },
            confirmButton = {
                TextButton(
                    onClick = {
                        openDialog = false
                        Toast.makeText(contextForToast, "Yes", Toast.LENGTH_SHORT).show()
                    }
                ) {
                    Text(text = "Yes")
                }
            },
            dismissButton = {
                TextButton(
                    onClick = {
                        openDialog = false
                        Toast.makeText(contextForToast, "No", Toast.LENGTH_SHORT).show()
                    }
                ) {
                    Text(text = "No")
                }
            },
            title = { Text(text = "Are you sure?") },
            text = { Text(text = "Are you sure you want to delete this image?") }
        )
    }

    Button(
        onClick = {
            openDialog = true
        }
    ) {
        Text(text = "Open Dialog")
    }
}

Output:

Alert Dialog Example

Dialog with Icon:

In Material 3, dialogs can display an icon above the title.

@Composable
fun MyUI() {
    var openDialog by remember {
        mutableStateOf(false)
    }

    val contextForToast = LocalContext.current.applicationContext

    if (openDialog) {
        AlertDialog(
            onDismissRequest = {
                openDialog = false
            },
            confirmButton = {
                TextButton(
                    onClick = {
                        openDialog = false
                        Toast.makeText(contextForToast, "Yes", Toast.LENGTH_SHORT).show()
                    }
                ) {
                    Text(text = "Yes")
                }
            },
            dismissButton = {
                TextButton(
                    onClick = {
                        openDialog = false
                        Toast.makeText(contextForToast, "No", Toast.LENGTH_SHORT).show()
                    }
                ) {
                    Text(text = "No")
                }
            },
            title = { Text(text = "Are you sure?") },
            text = { Text(text = "Are you sure you want to delete this image?") },
            icon = { Icon(imageVector = Icons.Default.Delete, contentDescription = null) } // add icon
        )
    }

    Button(
        onClick = {
            openDialog = true
        }
    ) {
        Text(text = "Open Dialog")
    }
}

Output:

Dialog with Icon

Related: Icons in Jetpack Compose

How to Create a Custom Dialog?

There is another overload of AlertDialog with content parameter.

@ExperimentalMaterial3Api
@Composable
fun AlertDialog(
    onDismissRequest: () -> Unit,
    modifier: Modifier = Modifier,
    properties: DialogProperties = DialogProperties(),
    content: @Composable () -> Unit
)

We can create custom dialogs using it. Currently, it is experimental.

Example:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyUI() {
    var openDialog by remember {
        mutableStateOf(false) // initially, don't show the dialog
    }

    var sliderValue by remember {
        mutableFloatStateOf(50f) // pass the initial value
    }

    if (openDialog) {
        AlertDialog(
            onDismissRequest = {
                openDialog = false
            }
        ) {
            // add the dialog's content here
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .background(
                        color = MaterialTheme.colorScheme.surface,
                        shape = RoundedCornerShape(size = 16.dp) // shape of the dialog
                    )
                    .padding(all = 16.dp), // inner padding
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                // heading
                Text(
                    text = "Select Your Weight (in Kgs)",
                    fontWeight = FontWeight.Bold
                )

                // slider container
                Row(
                    modifier = Modifier
                        .padding(top = 8.dp)
                        .fillMaxWidth(),
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.spacedBy(
                        space = 4.dp, // gap between children
                        alignment = Alignment.CenterHorizontally
                    )
                ) {
                    // minimum value
                    Text(
                        text = "20",
                        fontSize = 12.sp,
                        fontWeight = FontWeight.Medium
                    )

                    // slider
                    Slider(
                        modifier = Modifier.width(220.dp),
                        value = sliderValue,
                        onValueChange = { newValue ->
                            sliderValue = newValue
                        },
                        valueRange = 20f..101f
                    )

                    // maximum value
                    Text(
                        text = "100+",
                        fontSize = 12.sp,
                        fontWeight = FontWeight.Medium
                    )
                }

                // display the value
                Text(
                    modifier = Modifier.padding(top = 8.dp),
                    text = "${if (sliderValue >= 101) "100+" else sliderValue.toInt()} Kgs",
                    fontWeight = FontWeight.Bold
                )
            }
        }
    }

    Button(
        onClick = {
            openDialog = true
        }
    ) {
        Text(text = "Open Dialog")
    }
}

Output:

Custom Dialog Jetpack Compose

In the content block, We added a Column layout and set its corner size, inner padding, and width to the max size. Next, we added the heading.

In the Row layout, we put the two texts (minimum and maximum values) and a Slider. And lastly, the selected value is shown using the Text() composable.

How to Make a Full-Screen Dialog?

We can make the dialog full-screen using the dialog properties.

@Immutable
class DialogProperties constructor(
    val dismissOnBackPress: Boolean = true,
    val dismissOnClickOutside: Boolean = true,
    val securePolicy: SecureFlagPolicy = SecureFlagPolicy.Inherit,
    val usePlatformDefaultWidth: Boolean = true,
    val decorFitsSystemWindows: Boolean = true
)

dismissOnBackPress – If the dialog should be dismissed when the user presses the back button. If it is true, pressing the back button will call onDismissRequest.

dismissOnClickOutside – If the dialog should be dismissed when the user clicks outside the dialog. If it is true, clicking outside the dialog will call onDismissRequest.

securePolicy – Policy for setting FLAG_SECURE on the dialog’s window.

usePlatformDefaultWidth – If the dialog should use the platform’s default width which is smaller than the screen size. If you want to create a full screen dialog, set its value to false.

decorFitsSystemWindows – Whether to use WindowInsets.

Example:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyUI() {
    var openDialog by remember {
        mutableStateOf(false) // initially, don't show the dialog
    }

    if (openDialog) {
        AlertDialog(
            modifier = Modifier
                .fillMaxSize() // set the max size
                .background(color = MaterialTheme.colorScheme.surface),
            onDismissRequest = {
                openDialog = false
            },
            properties = DialogProperties(usePlatformDefaultWidth = false) // make the dialog full screen
        ) {
            // dialog content
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .background(color = Color.Green.copy(alpha = 0.3f)),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(text = "I'm a full screen dialog")
            }
        }
    }

    Button(
        onClick = {
            openDialog = true
        }
    ) {
        Text(text = "Open Dialog")
    }
}

Output:

Full Screen Dialog

Note: There is another API called DatePickerDialog(). It is used to display date pickers. For more information, look at the look at this article.

This is all about dialog APIs in Material 3 Jetpack Compose. I hope you have learned something new. If you have any doubts, leave a comment below.

Related Articles:


References:

Leave a Comment