import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.displayCutoutPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
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.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.composeunstyled.DialogPanel
import com.composeunstyled.Scrim
import com.composeunstyled.UnstyledButton
import com.composeunstyled.UnstyledDialog
import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds
@Composable
fun DialogDemo() {
var dialogVisible by remember { mutableStateOf(true) }
LaunchedEffect(dialogVisible) {
if (dialogVisible.not()) {
delay(1.seconds)
dialogVisible = true
}
}
UnstyledDialog(
visible = dialogVisible,
onDismissRequest = { dialogVisible = false },
overlay = {
Scrim(scrimColor = Color.Black.copy(0.3f), enter = fadeIn(), exit = fadeOut())
},
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
DialogPanel(
modifier = Modifier
.padding(20.dp)
.displayCutoutPadding()
.systemBarsPadding()
.widthIn(max = 560.dp)
.padding(20.dp)
.clip(RoundedCornerShape(12.dp))
.background(Color(0xFFF8FAFC))
.border(1.dp, Color(0xFFCACACA), RoundedCornerShape(12.dp)),
paneTitle = "Dialog",
enter = scaleIn(initialScale = 0.8f) + fadeIn(tween(durationMillis = 250)),
exit = scaleOut(targetScale = 0.6f) + fadeOut(tween(durationMillis = 150)),
) {
Column {
Column(Modifier.padding(start = 24.dp, top = 24.dp, end = 24.dp)) {
BasicText(
text = "Update Available",
style = TextStyle(
color = Color(0xFF1A1A1A),
fontSize = 16.sp,
lineHeight = 24.sp,
fontWeight = FontWeight.Medium,
),
)
Spacer(Modifier.height(8.dp))
BasicText(
text = "A new version of the app is available. " +
"Please update to the latest version.",
style = TextStyle(color = Color(0xFF1A1A1A)),
)
}
Spacer(Modifier.height(24.dp))
UnstyledButton(
onClick = { /* TODO */ },
modifier = Modifier
.padding(12.dp)
.align(Alignment.End)
.clip(RoundedCornerShape(6.dp)),
indication = LocalIndication.current,
) {
BasicText(
"Update",
modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp),
style = TextStyle(color = Color.Black),
)
}
}
}
}
}
}Features
- Modal focus behavior
- Outside-click dismiss
- Back and Escape dismiss
- Panel enter and exit transitions
Installation
implementation("com.composables:composeunstyled-dialog")
Anatomy
UnstyledDialog(
visible = visible,
onDismissRequest = { visible = false },
) {
DialogPanel {
}
}
Concepts
UnstyledDialogrenders dialog content in a modal layer.DialogPanelrenders the focusable dialog content.
Accessibility
Use the paneTitle parameter on DialogPanel when the dialog has a clear title.
Code Examples
Showing and hiding a dialog
Use the visible parameter to show the dialog and the onDismissRequest callback to update that state:
var visible by remember { mutableStateOf(false) }
BasicText(
text = "Show dialog",
modifier = Modifier.clickable { visible = true },
)
UnstyledDialog(
visible = visible,
onDismissRequest = { visible = false },
) {
DialogPanel {
BasicText("Dialog content")
}
}
Adding an overlay behind a dialog
Use the overlay parameter to render content behind the dialog panel. Scrim provides a ready-made overlay for dialogs.
UnstyledDialog(
visible = visible,
onDismissRequest = { visible = false },
overlay = { Scrim() },
) {
DialogPanel {
BasicText("Dialog content")
}
}
Changing dismiss behavior
Use the properties parameter to control how the dialog can be dismissed:
UnstyledDialog(
visible = visible,
onDismissRequest = { visible = false },
properties = DialogProperties(
dismissOnBackPress = false,
dismissOnClickOutside = false,
),
) {
DialogPanel {
BasicText("Dialog content")
}
}
Animating the dialog panel
Use the enter and exit parameters on DialogPanel to animate the dialog panel:
UnstyledDialog(
visible = visible,
onDismissRequest = { visible = false },
) {
DialogPanel(
enter = fadeIn(),
exit = fadeOut(),
) {
BasicText("Dialog content")
}
}
API Reference
UnstyledDialog
| Parameter | Type | Description |
|---|---|---|
visible |
Boolean |
|
onDismissRequest |
() -> Unit |
|
properties |
DialogProperties |
Properties that control when the dialog needs to be dismissed (such as clicking outside of the panel or pressing Esc or Back. |
overlay |
(DialogOverlayScope.() -> Unit)? |
|
content |
DialogScope.() -> Unit |
A @Composable function that provides a DialogScope. |
DialogOverlayScope.Scrim
| Parameter | Type | Description |
|---|---|---|
modifier |
Modifier |
Modifier for the Scrim |
scrimColor |
Color |
Controls the color of the Scrim. The default color is Black with an alpha of 60%. |
enter |
EnterTransition |
The EnterTransition when the Scrim enters the composition |
exit |
ExitTransition |
The ExitTransition when the Scrim enters the composition |
DialogScope.DialogPanel
| Parameter | Type | Description |
|---|---|---|
modifier |
Modifier |
Modifier for the Scrim |
paneTitle |
String? |
|
enter |
EnterTransition |
The EnterTransition when the Scrim enters the composition |
exit |
ExitTransition |
The ExitTransition when the Scrim enters the composition |
content |
() -> Unit |
A @Composable function that provides a DialogScope. |