Gradient Warning Dialog KMP
Create a responsive warning dialog UI in Jetpack Compose, compatible with Kotlin Multiplatform (KMP), featuring animations, adaptive layout for orientations, and confirm/cancel functionality.
Features
Learn to implement a customizable responsive warning dialog UI with fade-in and scale animations, gradient styling, and orientation-aware design, optimized for KMP projects.
- Responsive Layout: Automatically adapts to portrait, landscape, and desktop using BoxWithConstraints.
- Animations: Smooth fade-in and scale-in effects for engaging user experience.
- Customizable: Parameters for title, message, and button actions.
- KMP Compatible: Utilizes Compose Multiplatform for cross-platform support.
Step-by-Step Implementation
Follow these steps to create a Gradient Warning Dialog UI in your KMP project:
- Define the Composable: Create
GradientWarningDialog
with parameters for title, message, dismiss, and confirm callbacks. - Animate Fade and Scale: Use
animateFloatAsState
for alpha and scale animations. - Build Main Layout: Use
BoxWithConstraints
to detect orientation and apply animations. - Icon Section: Display a warning icon with scaling for desktop.
- Text Section: Add title and message with responsive font sizes.
- Button Section: Implement adaptive button layout for small and large screens.
- Define Constants: Create object for styling and animation durations.
- Demo Composable: Create a demo screen to showcase the dialog.
Step 1: Define the GradientWarningDialog Composable
Start by defining the composable with customizable parameters.
@Composable
fun GradientWarningDialog(
title: String = "Warning",
message: String = "This action cannot be undone. Proceed with caution!",
onDismiss: () -> Unit,
onConfirm: () -> Unit = {},
modifier: Modifier = Modifier
) {
val (alphaAnim, scaleAnim) = animateFadeAndScaleDialog()
Dialog(
onDismissRequest = onDismiss,
properties = DialogProperties(dismissOnClickOutside = true)
) {
BoxWithConstraints(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(DialogConstants.PADDING)
) {
// Further implementation
}
}
}
Step 2: Animate Fade and Scale
Use remembered animation states for fade-in and scale-in effects.
@Composable
private fun animateFadeAndScaleDialog(): Pair {
val alpha by animateFloatAsState(
targetValue = 1f,
animationSpec = tween(
durationMillis = DialogConstants.ALPHA_DURATION,
easing = FastOutSlowInEasing
),
label = "alpha"
)
val scale by animateFloatAsState(
targetValue = 1f,
animationSpec = tween(
durationMillis = DialogConstants.SCALE_DURATION,
easing = FastOutSlowInEasing
),
label = "scale"
)
return alpha to scale
}
Step 3: Build Main Layout
Use BoxWithConstraints
for orientation detection and apply gradient background.
BoxWithConstraints(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(DialogConstants.PADDING)
) {
val isLandscape = maxWidth > maxHeight
val isDesktop = maxWidth > 800.dp
val isSmallScreen = maxWidth < 400.dp
val dialogWidth = when {
isDesktop -> maxWidth * 0.55f
isLandscape -> maxWidth * 0.75f
else -> maxWidth * 0.9f
}
val maxDialogHeight = if (isDesktop) 500.dp else 400.dp
Card(
modifier = Modifier
.width(dialogWidth)
.wrapContentHeight()
.heightIn(max = maxDialogHeight)
.alpha(alphaAnim)
.scale(scaleAnim),
shape = RoundedCornerShape(DialogConstants.CORNER_RADIUS),
colors = CardDefaults.cardColors(containerColor = Color.Transparent),
elevation = CardDefaults.cardElevation(defaultElevation = 8.dp)
) {
Column(
modifier = Modifier
.background(
Brush.linearGradient(
colors = listOf(
Color(0xFFFF8A65),
Color(0xFFF06292)
)
)
)
.padding(DialogConstants.CONTENT_PADDING),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween
) {
// Further implementation
}
}
}
Step 4: Icon Section
Display a warning icon with desktop scaling and animation.
@Composable
private fun WarningIconSection(isDesktop: Boolean) {
val iconSize = if (isDesktop) 96.dp else DialogConstants.ICON_SIZE
val iconPadding = if (isDesktop) 16.dp else DialogConstants.ICON_PADDING
val iconScale by animateFloatAsState(
targetValue = 1f,
animationSpec = tween(
durationMillis = DialogConstants.ICON_SCALE_DURATION,
easing = FastOutSlowInEasing
),
label = "iconScale"
)
Icon(
imageVector = Icons.Rounded.Warning,
contentDescription = "Warning",
tint = Color.White,
modifier = Modifier
.size(iconSize)
.scale(if (isDesktop) iconScale else 1f)
.padding(bottom = iconPadding)
)
}
Step 5: Text Section
Add title and message with responsive font sizes.
@Composable
private fun TextSection(title: String, message: String, isDesktop: Boolean) {
val titleFontSize = if (isDesktop) DialogConstants.TITLE_FONT_SIZE * 1.2f else DialogConstants.TITLE_FONT_SIZE
val messageFontSize = if (isDesktop) DialogConstants.MESSAGE_FONT_SIZE * 1.2f else DialogConstants.MESSAGE_FONT_SIZE
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = title,
color = Color.White,
fontSize = titleFontSize,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier.padding(horizontal = DialogConstants.CONTENT_PADDING)
)
Spacer(modifier = Modifier.height(DialogConstants.SPACING))
Text(
text = message,
color = Color.White.copy(alpha = 0.95f),
fontSize = messageFontSize,
textAlign = TextAlign.Center,
lineHeight = messageFontSize * 1.4f,
modifier = Modifier.padding(horizontal = DialogConstants.CONTENT_PADDING)
)
}
}
Step 6: Button Section
Implement adaptive button layout for small and large screens.
@Composable
private fun ButtonSection(onDismiss: () -> Unit, onConfirm: () -> Unit, isSmallScreen: Boolean) {
if (isSmallScreen) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(top = DialogConstants.SPACING),
horizontalAlignment = Alignment.CenterHorizontally
) {
DialogButton(
text = "Cancel",
onClick = onDismiss,
textColor = Color(0xFFF06292),
modifier = Modifier
.fillMaxWidth(0.85f)
.height(DialogConstants.BUTTON_HEIGHT)
.padding(bottom = DialogConstants.BUTTON_PADDING)
)
DialogButton(
text = "Proceed",
onClick = {
onConfirm()
onDismiss()
},
textColor = Color(0xFFFF8A65),
modifier = Modifier
.fillMaxWidth(0.85f)
.height(DialogConstants.BUTTON_HEIGHT)
)
}
} else {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = DialogConstants.SPACING),
horizontalArrangement = Arrangement.SpaceEvenly
) {
DialogButton(
text = "Cancel",
onClick = onDismiss,
textColor = Color(0xFFF06292),
modifier = Modifier
.weight(1f)
.height(DialogConstants.BUTTON_HEIGHT)
.padding(horizontal = DialogConstants.BUTTON_PADDING)
)
DialogButton(
text = "Proceed",
onClick = {
onConfirm()
onDismiss()
},
textColor = Color(0xFFFF8A65),
modifier = Modifier
.weight(1f)
.height(DialogConstants.BUTTON_HEIGHT)
.padding(horizontal = DialogConstants.BUTTON_PADDING)
)
}
}
}
@Composable
private fun DialogButton(
text: String,
onClick: () -> Unit,
textColor: Color,
modifier: Modifier = Modifier
) {
Button(
onClick = onClick,
colors = ButtonDefaults.buttonColors(
containerColor = Color.White,
contentColor = textColor
),
shape = RoundedCornerShape(8.dp),
modifier = modifier
) {
Text(
text = text,
fontSize = 16.sp,
fontWeight = FontWeight.Medium
)
}
}
Step 7: Define Styling Constants
Create constants for consistent styling and animation timing.
private object DialogConstants {
val PADDING = 16.dp
val CONTENT_PADDING = 20.dp
val CORNER_RADIUS = 16.dp
val ICON_SIZE = 48.dp
val ICON_PADDING = 8.dp
val SPACING = 12.dp
val BUTTON_PADDING = 8.dp
val BUTTON_HEIGHT = 48.dp
val TITLE_FONT_SIZE = 20.sp
val MESSAGE_FONT_SIZE = 16.sp
const val ALPHA_DURATION = 300
const val SCALE_DURATION = 250
const val ICON_SCALE_DURATION = 350
}
Step 8: Demo Composable
Create a demo screen to showcase the dialog.
@Composable
fun GradientWarningDialogDemo() {
var showDialog by remember { mutableStateOf(false) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "Gradient Warning Dialog Demo",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier.padding(bottom = 8.dp)
)
Button(
onClick = { showDialog = true },
modifier = Modifier
.wrapContentWidth()
.padding(top = 16.dp)
) {
Text("Show Warning Dialog")
}
if (showDialog) {
GradientWarningDialog(
onDismiss = { showDialog = false },
onConfirm = { /* Add your confirm logic here */ }
)
}
}
}
Full Source Code
Use the Gradient Warning Dialog UI to provide user-friendly warnings in KMP projects for 2025.
Examples:
- Display warning dialogs in cross-platform apps
- Handle critical actions on Android or iOS
- Provide confirm/cancel options across platforms