--- title: Dialog description: An unstyled Dialog component that can be used to implement Dialogs with the styling of your choice. Supports edge-to-edge rendering, enter/exit transitions and keyboard interactions out of the box. --- {{unstyled_demo:dialog}} ## Basic Example A dialog consists of the following components: `Dialog`, `DialogPanel` and the optional `Scrim`. The `Dialog` controls the visibility of the dialog via the `DialogState` object. The `DialogPanel` is a container component that renders the dialog's panel and its contents. The optional `Scrim` component is used to add layer behind the dialog and dim the rest of the UI. ```kotlin val dialogState = rememberDialogState() Box { Button(onClick= { dialogState.visible = true }) { Text("Show Dialog") } Dialog(state = dialogState) { DialogPanel( modifier = Modifier .displayCutoutPadding() .systemBarsPadding() .widthIn(min = 280.dp, max = 560.dp) .padding(20.dp) .clip(RoundedCornerShape(12.dp)) .border(1.dp, Color(0xFFE4E4E4), RoundedCornerShape(12.dp)) .background(Color.White), ) { Column { Column(Modifier.padding(start = 24.dp, top = 24.dp, end = 24.dp)) { Text( text = "Update Available", style = TextStyle(fontWeight = FontWeight.Medium) ) Spacer(Modifier.height(8.dp)) Text( text = "A new version of the app is available. Please update to the latest version.", style = TextStyle(color = Color(0xFF474747)) ) } Spacer(Modifier.height(24.dp)) Button( onClick = { /* TODO */ }, modifier = Modifier.padding(12.dp).align(Alignment.End), shape = RoundedCornerShape(4.dp), contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp) ) { Text( text = "Update", color = Color(0xFF6699FF) ) } } } } } ``` ## Styling Any sort of styling is done by the `Modifier` of the respective component. Changing the looks of the dialog's panel is done by passing the respective styling `Modifier`s to your `DialogPanel`: ```kotlin Dialog(state = rememberDialogState(visible = true)) { DialogPanel( modifier = Modifier.systemBarsPadding() .widthIn(min = 280.dp, max = 560.dp) .padding(20.dp) .clip(RoundedCornerShape(12.dp)) .border(1.dp, Color(0xFFE4E4E4), RoundedCornerShape(12.dp)) .background(Color.White) ) { Column { Text("Something important happened") Button(onClick = { /* TODO */ }) { Text("Got it") } } } } ``` ## Code Examples ### Showing/Hide the dialog Pass your own `DialogState` to the `Dialog` and change the *visible* property according to your needs: ```kotlin val state = rememberDialogState() Button(onClick = { state.visible = true }) { Text("Show dialog") } Dialog(state = state) { DialogPanel { Column { Text("Something important happened") Button(onClick = { state.visible = false }) { Text("Got it") } } } } ``` The Dialog will also be automatically dismissed by default if the user taps outside the DialogPanel or presses the ' Escape' or 'Back' button on their device. In override to override this behavior pass the `DialogProperties` object to the Dialog with the desired properties: ```kotlin Dialog( state = rememberDialogState(), properties = DialogProperties(dismissOnClickOutside = false, dismissOnBackPress = false) ) { // TODO the rest of your dialog } ``` ### Adding a scrim Use the `Scrim` component within your `Dialog`: ```kotlin val state = rememberDialogState() Button(onClick = { state.visible = true }) { Text("Show dialog") } Dialog(state = state) { Scrim() DialogPanel { Column { Text("Something important happened") Button(onClick = { state.visible = false }) { Text("Got it") } } } } ``` The `Scrim` is customizable and you can pass any *scrimColor*, and *enter*/*exit* transitions that matches your design specs. ### Scrollable dialogs Add any scrollable component such as `LazyColumn` to the contents of your dialog: ```kotlin val state = rememberDialogState() Button(onClick = { state.visible = true }) { Text("Show dialog") } Dialog(state = state) { DialogPanel { Column { LazyColumn(Modifier.height(320.dp)) { item { Text("Something important happened") } repeat(100) { i -> item { Text("Update number ${i}") } } } Button(onClick = { state.visible = false }) { Text("Got it") } } } } ``` ### Full-screen dialogs Pass a `Modifier.fillMaxSize()` to the `DialogPanel`'s modifier parameter. Make sure to pass the `Modifier.systemBarsPadding()` and `Modifier.displayCutoutPadding()` or any related inset Modifier so that the dialog is not drawn behind any system bars (such as status and navigation bar on Android): ```kotlin Dialog(state = rememberDialogState(visible = true)) { DialogPanel( modifier = Modifier .displayCutoutPadding() .systemBarsPadding() .fillMaxSize() ) { Column { Text("This is a full screen dialog") Button(onClick = { state.visible = false }) { Text("Got it") } } } } ``` ### Adding transitions Add any *enter*/*exit* transitions into the `DialogPanel` and `Scrim` to control how they appear on the screen when they enter and exit the composition: ```kotlin Dialog(state = rememberDialogState(visible = true)) { Scrim(enter = fadeIn(), exit = fadeOut()) DialogPanel( enter = scaleIn(initialScale = 0.8f) + fadeIn(tween(durationMillis = 250)), exit = scaleOut(targetScale = 0.6f) + fadeOut(tween(durationMillis = 150)), ) { // TODO the dialog's contents } } ``` ### Styling the system bars **Android only** Dialogs will not change the color of your system UI when displayed. We provide a `LocalModalWindow` composition local, which provides you with the Android `Window` that hosts the dialog, so that you can customize the System UI according to your needs. This is part of the [Modal](modal.md) API: ```kotlin Dialog(rememberDialogState()) { DialogPanel { val window = LocalModalWindow.current LaunchedEffect(Unit) { // change system bars to transparent window.statusBarColor = Color.Transparent.toArgb() window.navigationBarColor = Color.Transparent.toArgb() // don't forget to update the icons too val windowInsetsController = WindowInsetsControllerCompat(window, window.decorView) windowInsetsControllerCompat.isAppearanceLightStatusBars = true windowInsetsControllerCompat.isAppearanceLightNavigationBars = true } Text("Transparent bars. So cool 😎 ", modifier = Modifier.navigationBarsPadding()) } } ``` ## Keyboard Interactions | Key | Description | |---------------------------------------------|----------------------------------------------------------------------------------------------| |
Esc
| Closes any open dialogs, if the *dismissOnBackPress* of `DialogProperties()` is set to true. | |
Back
| Closes any open dialogs, if the *dismissOnBackPress* of `DialogProperties()` is set to true. | |
Tab
| Cycles through the dialog's contents. | |
Shift + Tab
| Cycles through the dialog's contents in backwards order. | ## Parameters ### Dialog The main component. | Parameter | Description | |--------------|-------------------------------------------------------------------------------------------------------------------------------| | `state` | A `DialogState` object which controls the visibility of the dialog. | | `properties` | Properties that control when the dialog needs to be dismissed (such as clicking outside of the panel or pressing Esc or Back. | | `onDismiss` | Called when the dialog is being dismissed either by tapping outside or by pressing `Esc` or `Back`. | | `content` | A `@Composable` function that provides a `DialogScope`. | ### DialogPanel The visual representation of your dialog. Can only be used from a `DialogScope`. | Parameter | Description | |--------------|-------------------------------------------------------------------------------------------------------------------------------| | `state` | A `DialogState` object which controls the visibility of the dialog. | | `properties` | Properties that control when the dialog needs to be dismissed (such as clicking outside of the panel or pressing Esc or Back. | | `content` | A `@Composable` function that provides a `DialogScope`. | ### Scrim The dimming layer that is often placed behind a `DialogPanel`, to let the user focus on the dialog. Can only be used from a `DialogScope`. | Parameter | Description | |--------------|-----------------------------------------------------------------------------------| | `modifier` | `Modifier` for the Scrim | | `scrimColor` | Controls the color of the Scrim. The default color is Black with an alpha of 60%. | | `enter` | The `EnterTransition` when the Scrim enters the composition | | `exit` | The `ExitTransition` when the Scrim enters the composition | ## Vanilla Dialog vs Unstyled Dialog Compose's original Dialog component does not support custom animations. Even though it is possible to animate it, it requires you to combine multiple components together and sync state to animations to composition which is not straightforward to do. In addition, the Jetpack Compose (Android) Dialog comes with the original Android's dialog width and inset constraints, which are historically a pain to deal with and customize. Unstyled's Dialog is designed to be customizable inside out and work the same way on every platform without surprises. It behaves like any other component. If for example you need a full screen dialog, all you have to do is pass `Modifier.fillMaxSize()` to the `DialogPanel`, without having to worry about platform flags.