Rameshbabu
Rameshbabu

Reputation: 473

Position Alert dialog in Android Compose

How to position alert dialog in Jetpack Compose to the bottom of the screen. Also, set transparent background.

Upvotes: 15

Views: 12192

Answers (6)

LinuxFelipe-COL
LinuxFelipe-COL

Reputation: 411

To avoid breaking the Dialog behaviour dismissOnClickOutside

Do:

@Composable
fun YourDialog(onDismissRequest: () -> Unit) {
    Dialog(
        onDismissRequest = {
            onDismissRequest()
        },
        DialogProperties(usePlatformDefaultWidth = false), // Full width dialog
    ) {
        EditDialogWindow()
        Content(onDismissRequest)// you can apply Modifier.clickable{ onDismissReques } on your Dialog content
    }
}

@Composable
fun EditDialogWindow() {
    val dialogWindowProvider = LocalView.current.parent as? DialogWindowProvider

    dialogWindowProvider?.window?.let { window ->
        window.setGravity(Gravity.BOTTOM) // Position your dialog
        window.setDimAmount(0f) // Extra: Remove black background of Dialog
    }
}

You can modify the gravity with as you wish: // Extra: How to add multiple positions? window.setGravity(Gravity.BOTTOM or Gravity.END)

Upvotes: 1

MotohawkSF
MotohawkSF

Reputation: 2603

FYI, one easy way to get a dialog with padding (as well as alignment) without compromising 'dismiss on click outside' (a problem with an align-in-a-Box solution):

fun MyTopAlignedDialog(
    onDismissRequest: () -> Unit,
) {
    Dialog(
        onDismissRequest = onDismissRequest,
        //...
    ) {
        (LocalView.current.parent as DialogWindowProvider).apply {
            window.setGravity(Gravity.TOP)
            window.attributes = window.attributes.apply {
                y = DialogTopPadding
            }
        }

        // Dialog content...
    }
}

internal const val DialogTopPadding = 192

Upvotes: 1

JJGuerrero
JJGuerrero

Reputation: 609

The solution that worked best for me, getting the idea from Vladimir's response, is to get a reference to the window from within the Dialog and set the desired gravity.

Dialog(
    onDismissRequest = [...],
    properties = [...],
) {
    val dialogWindowProvider = LocalView.current.parent as DialogWindowProvider
    dialogWindowProvider.window.setGravity(Gravity.BOTTOM)
    [...]
}

The cast shouldn't be an issue but, of course, it could be safe-casted to prevent unpleasant surprises.

Upvotes: 23

Vladimir Borutsky
Vladimir Borutsky

Reputation: 84

The solution provided by @Rameshbabu changes the position of the dialog as expected. However, as was mentioned in the comments the dialog consequently becomes "fullscreen" and it can't be dismissed by tapping on the overlay.

I found another tricky solution. You can copy the source code of androidx.compose.ui.window.AndroidDialog.kt which comes with "androidx.compose.ui". By doing this you'll be able to access the dialog's window directly and change its gravity and other parameters.

The simplest way would be to add some code in the androidx.compose.ui.window.DialogWrapper class. For example in the init function:

private class DialogWrapper(
    private var onDismissRequest: () -> Unit,
    ...
) : Dialog(ContextThemeWrapper(...){

...

init {
    val window = window ?: error("Dialog has no window")
    window.requestFeature(Window.FEATURE_NO_TITLE)
    window.setBackgroundDrawableResource(android.R.color.transparent)
    window.setGravity(Gravity.TOP) // This to be added

Hopefully, the Jetpack Compose team will make the compose Dialog more customizable in the future.

Upvotes: -1

Rameshbabu
Rameshbabu

Reputation: 473

Thanks, @alekseyHunter & @johann. I can able to achieve this with custom layout modifier.

Custom modifier to position alert dialog

enum class CustomDialogPosition {
    BOTTOM, TOP
}

fun Modifier.customDialogModifier(pos: CustomDialogPosition) = layout { measurable, constraints ->

    val placeable = measurable.measure(constraints);
    layout(constraints.maxWidth, constraints.maxHeight){
        when(pos) {
            CustomDialogPosition.BOTTOM -> {
                placeable.place(0, constraints.maxHeight - placeable.height, 10f)
            }
            CustomDialogPosition.TOP -> {
                placeable.place(0,0,10f)
            }
        }
    }
}

And in alert dialog implementation as

AlertDialog( ..., modifiers = Modifiers.customDialogModifier(CustomDialogPosition.BOTTOM)) 
{
    // block
})

Upvotes: 10

alekseyHunter
alekseyHunter

Reputation: 418

Look so simple.

Box(Modifier.fillMaxSize()) {
            Column() {
                /* Content */
            }
             /* Box alert */
            Box(
                Modifier
                    .padding(horizontal = 32.dp, vertical = 16.dp)
                    .fillMaxWidth()
                    .background(Color.Transparent, RoundedCornerShape(8.dp))
                    .border(2.dp, Color.LightGray, RoundedCornerShape(8.dp))
                    .align(Alignment.BottomCenter)
            ) {
                Text(
                    text = "Alert",
                    modifier = Modifier
                        .padding(16.dp)
                        .fillMaxWidth(),
                    textAlign = TextAlign.Center,
                    color = Color.Black
                )
            }
        }

Upvotes: 0

Related Questions