42

I tried to make a fullscreen dialog using Jetpack Compose using this code:

Dialog(onDismissRequest = { /*TODO*/ }) {
           NewPostDialog()
       }

It ended up looking something like this. How can I remove the margin at the side (marked red)?

Example

5 Answers 5

102
+50

UPDATE: As @Nestor Perez mentioned, since compose 1.0.0-rc01 you can set usePlatformDefaultWidthin DialogProperties to make a dialog fill the whole screenwidth:

Dialog(
    properties = DialogProperties(usePlatformDefaultWidth = false),
    onDismissRequest...
    ){
      Surface(modifier = Modifier.fillMaxSize()) {
          DialogContent()
      }
    }

Compose Dialog uses ContextThemeWrapper so you should be able to theme your dialog with a custom style.

themes.xml:

 <style name="Theme.YourApp" parent="Theme.MaterialComponents.Light.NoActionBar">
       //theme content...
        <item name="android:dialogTheme">@style/Theme.DialogFullScreen</item>
    </style>

 <style name="Theme.DialogFullScreen" parent="@style/ThemeOverlay.MaterialComponents.Dialog.Alert">
        <item name="android:windowMinWidthMajor">100%</item>
        <item name="android:windowMinWidthMinor">100%</item>
    </style>

And in code:

@Composable
fun FullScreenDialog(showDialog:Boolean, onClose:()->Unit) {
    if (showDialog) {
        Dialog(onDismissRequest =  onClose ) {
            Surface(
                modifier = Modifier.fillMaxSize(),
                shape = RoundedCornerShape(16.dp),
                color = Color.LightGray
            ) {
                Box(
                    contentAlignment = Alignment.Center
                ) {
                    Text(modifier = Modifier.align(Alignment.TopCenter),
                        text = "top")
                    Text("center")
                    Text(
                        modifier = Modifier.align(Alignment.BottomCenter),
                        text = "bottom")
                }
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

It works, Thanks! Is there a way to only apply the theme to a particular dialog instead of the whole app?
I've tried to update AmbientContext with a ContextThemeWrapper, without success. Maybe you can use a combination of fragments and compose where you set the theme in your fragment layout
Height is not getting set to full screen height, some part of bottom section shows the content behind the dialog
There is an issue in the android issue tracker to set a theme for a particular dialog: issuetracker.google.com/issues/198064319
14

If you want Jetpack compose full screen dialog that covers entire screen, draws under system bars (status and navigation bar), and supports immersive mode, that is officially unsupported yet but I found a work around:

  1. Find Activity and Dialog window (they are 2 different windows) and apply flags from activity window to dialog window, this will allow dialog window to draw under system bars
  2. Update dialog view parent layout params to match full screen
  3. Make system bars transparent so dialog content is visible underneath

Util methods

// Window utils
@Composable
fun getDialogWindow(): Window? = (LocalView.current.parent as? DialogWindowProvider)?.window

@Composable
fun getActivityWindow(): Window? = LocalView.current.context.getActivityWindow()

private tailrec fun Context.getActivityWindow(): Window? =
    when (this) {
        is Activity -> window
        is ContextWrapper -> baseContext.getActivityWindow()
        else -> null
    }

Full screen dialog

@Composable
fun DialogFullScreen(
    onDismissRequest: () -> Unit,
    properties: DialogProperties = DialogProperties(),
    content: @Composable () -> Unit
) {
    Dialog(
        onDismissRequest = onDismissRequest,
        properties = DialogProperties(
            dismissOnBackPress = properties.dismissOnBackPress,
            dismissOnClickOutside = properties.dismissOnClickOutside,
            securePolicy = properties.securePolicy,
            usePlatformDefaultWidth = true, // must be true as a part of work around
            decorFitsSystemWindows = false
        ),
        content = {
            val activityWindow = getActivityWindow()
            val dialogWindow = getDialogWindow()
            val parentView = LocalView.current.parent as View
            SideEffect {
                if (activityWindow != null && dialogWindow != null) {
                    val attributes = WindowManager.LayoutParams()
                    attributes.copyFrom(activityWindow.attributes)
                    attributes.type = dialogWindow.attributes.type
                    dialogWindow.attributes = attributes
                    parentView.layoutParams = FrameLayout.LayoutParams(activityWindow.decorView.width, activityWindow.decorView.height)
                }
            }

            val systemUiController = rememberSystemUiController(getActivityWindow())
            val dialogSystemUiController = rememberSystemUiController(getDialogWindow())

            DisposableEffect(Unit) {
                systemUiController.setSystemBarsColor(color = Color.Transparent)
                dialogSystemUiController.setSystemBarsColor(color = Color.Transparent)

                onDispose {
                    systemUiController.setSystemBarsColor(color = previousSystemBarsColor)
                    dialogSystemUiController.setSystemBarsColor(color = previousSystemBarsColor)
                }
            }

            // If you need Immersive mode
            val isImmersive = true
            DisposableEffect(isImmersive) {
                systemUiController.isSystemBarsVisible = !isImmersive
                dialogSystemUiController.isSystemBarsVisible = !isImmersive

                onDispose {
                    systemUiController.isSystemBarsVisible = true
                    dialogSystemUiController.isSystemBarsVisible = true
                }
            }

            Surface(modifier = Modifier.fillMaxSize(), color = Color.Transparent) {
                content()
            }
        }
    )
}

Full screen dialog with navigation

fun NavGraphBuilder.dialogFullScreen(
    route: String,
    arguments: List<NamedNavArgument> = emptyList(),
    deepLinks: List<NavDeepLink> = emptyList(),
    dialogProperties: DialogProperties = DialogProperties(),
    content: @Composable (NavBackStackEntry) -> Unit
) {
    dialog(
        route = route,
        arguments = arguments,
        deepLinks = deepLinks,
        dialogProperties = DialogProperties(
            dismissOnBackPress = dialogProperties.dismissOnBackPress,
            dismissOnClickOutside = dialogProperties.dismissOnClickOutside,
            securePolicy = dialogProperties.securePolicy,
            usePlatformDefaultWidth = true, // must be true as a part of work around
            decorFitsSystemWindows = false
        ),
        content = {
            val activityWindow = getActivityWindow()
            val dialogWindow = getDialogWindow()
            val parentView = LocalView.current.parent as View
            SideEffect {
                if (activityWindow != null && dialogWindow != null) {
                    val attributes = WindowManager.LayoutParams()
                    attributes.copyFrom(activityWindow.attributes)
                    attributes.type = dialogWindow.attributes.type
                    dialogWindow.attributes = attributes
                    parentView.layoutParams = FrameLayout.LayoutParams(activityWindow.decorView.width, activityWindow.decorView.height)
                }
            }

            val systemUiController = rememberSystemUiController(getActivityWindow())
            val dialogSystemUiController = rememberSystemUiController(getDialogWindow())

            DisposableEffect(Unit) {
                systemUiController.setSystemBarsColor(color = Color.Transparent)
                dialogSystemUiController.setSystemBarsColor(color = Color.Transparent)

                onDispose {
                    systemUiController.setSystemBarsColor(color = previousSystemBarsColor)
                    dialogSystemUiController.setSystemBarsColor(color = previousSystemBarsColor)
                }
            }

            // If you need Immersive mode
            val isImmersive = true
            DisposableEffect(isImmersive) {
                systemUiController.isSystemBarsVisible = !isImmersive
                dialogSystemUiController.isSystemBarsVisible = !isImmersive

                onDispose {
                    systemUiController.isSystemBarsVisible = true
                    dialogSystemUiController.isSystemBarsVisible = true
                }
            }
            
            Surface(modifier = Modifier.fillMaxSize(), color = Color.Transparent) {
                content(it)
            }
        }
    )
}

2 Comments

Thanks bro. But, carefully when copy attributes from activityWindow. For me, after copy attributes, i need change some properties of dialog windows because i edited activity befory. There are: isNavigationBarContrastEnforced = false isStatusBarContrastEnforced = false setBackgroundDrawableResource(android.R.color.transparent)
It's working for me, shame that SystemUIController in Accompanist is deprecated. The new replacement method just doesn't work.
6

Solution from jns didnt work too well form me, I leave another solution here if anyone is still looking:

Implement the theme as jns answer:

<style name="Theme.Outlay" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
    ...
    <!-- Customize your theme here. -->
    <item name="android:dialogTheme">@style/Theme.DialogFullScreen</item    >
</style>

<style name="Theme.DialogFullScreen" parent="@style/ThemeOverlay.MaterialComponents.Dialog.Alert">
    <item name="android:windowMinWidthMajor">100%</item>
    <item name="android:windowMinWidthMinor">100%</item>
</style>

For the dialog create an scaffold and add the experimental property usePlatformDefaultWidth = false on dialog properties:

Dialog(
    onDismissRequest = onBackPressed,
    properties = DialogProperties(
        usePlatformDefaultWidth = false
    )
) {
    Scaffold(topBar = { TopBar(onBackPressed = onBackPressed) }) {
        Content()
    }
}

2 Comments

It doesn't work well for me, only internal dialog space is increased, but not the dialog itself. So, part of it becomes hidden.
UPD: The problem is in preview mode only.
4

If you want to avoid having an xml theme entirely and also avoid doing this for all dialogs, you can set a requiredWidth modifier to be equal to LocalConfiguration.current.screenWidthDp.dp (multiplied by some fraction as you please).

An example that takes up 0.96f of the screen width:

@Composable
fun LargerDialog(
    dialogOpen: MutableState<Boolean>
) {
    Dialog(onDismissRequest = { dialogOpen.value = false }) {
        Card( // or Surface
            elevation = 8.dp,
            modifier = Modifier
                .requiredWidth(LocalConfiguration.current.screenWidthDp.dp * 0.96f)
                .padding(4.dp)
        ) {
            // content
        }
    }
}

4 Comments

doesn't work , I've even set the decorFitsSystemWindows to false in dialog properties but still bars show up with content completely inside
Works perfect for me, don't forget to add Modifer.fillMaxHeight.
PS: Don't pass MutableState as argument, use callback
For this solution work with split screen you also need to add ``` properties = DialogProperties(usePlatformDefaultWidth = false) ``` to the Dialog
1

To make a full-screen dialog using Jetpack Compose using this code: EG1: Full screen dialog screenshot

package compose.material.theme

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import compose.material.theme.ui.theme.Material3ComposeTheme



class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Material3ComposeTheme {
                val openFullDialogCustom = remember { mutableStateOf(false) }

                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {

                    Column(
                        modifier = Modifier
                            .padding(20.dp)
                            .verticalScroll(rememberScrollState())
                    ) {

                        //...................................................................
                        // * full screen custom dialog
                        Button(
                            onClick = {
                                openFullDialogCustom.value = true
                            },
                            modifier = Modifier.align(Alignment.CenterHorizontally)
                        ) {
                            Text(text = "No internet",style = MaterialTheme.typography.labelLarge)
                        }
                    }
                }

                //...............................................................................
                //Full screen Custom Dialog Sample
                NoInternetScreen(openFullDialogCustom)

            }
        }
    }


    @OptIn(ExperimentalComposeUiApi::class)
    @Composable
    private fun NoInternetScreen(openFullDialogCustom: MutableState<Boolean>) {
        if (openFullDialogCustom.value) {

            Dialog(
                onDismissRequest = {
                    openFullDialogCustom.value = false
                },
                properties = DialogProperties(
                    usePlatformDefaultWidth = false // experimental
                )
            ) {
                Surface(modifier = Modifier.fillMaxSize()) {

                    Column(
                        modifier = Modifier.fillMaxSize(),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {



                        Image(
                            painter = painterResource(id = R.drawable.no_intrenet),
                            contentDescription = null,
                            contentScale = ContentScale.Fit,
                            modifier = Modifier
                                .height(200.dp)
                                .fillMaxWidth(),

                            )

                        Spacer(modifier = Modifier.height(20.dp))
                        //.........................Text: title
                        Text(
                            text = "Whoops!!",
                            textAlign = TextAlign.Center,
                            modifier = Modifier
                                .padding(top = 20.dp)
                                .fillMaxWidth(),
                            letterSpacing = 2.sp,
                            fontWeight = FontWeight.Bold,
                            style = MaterialTheme.typography.titleLarge,
                            color = MaterialTheme.colorScheme.primary,
                        )
                        Spacer(modifier = Modifier.height(8.dp))

                        //.........................Text : description
                        Text(
                            text = "No Internet connection was found. Check your connection or try again.",
                            textAlign = TextAlign.Center,
                            modifier = Modifier
                                .padding(top = 10.dp, start = 25.dp, end = 25.dp)
                                .fillMaxWidth(),
                            letterSpacing = 1.sp,
                            style = MaterialTheme.typography.bodyLarge,
                            color = MaterialTheme.colorScheme.primary,
                        )
                        //.........................Spacer
                        Spacer(modifier = Modifier.height(24.dp))

                        val cornerRadius = 16.dp
                        val gradientColor = listOf(Color(0xFFff669f), Color(0xFFff8961))
                        GradientButton(
                            gradientColors = gradientColor,
                            cornerRadius = cornerRadius,
                            nameButton = "Try again",
                            roundedCornerShape = RoundedCornerShape(topStart = 30.dp,bottomEnd = 30.dp)
                        )

                    }

                }
            }

        }
    }


}

//...........................................................................
@Composable
fun GradientButton(
    gradientColors: List<Color>,
    cornerRadius: Dp,
    nameButton: String,
    roundedCornerShape: RoundedCornerShape
) {

    Button(
        modifier = Modifier
            .fillMaxWidth()
            .padding(start = 32.dp, end = 32.dp),
        onClick = {
            //your code
        },

        contentPadding = PaddingValues(),
        colors = ButtonDefaults.buttonColors(
            containerColor = Color.Transparent
        ),
        shape = RoundedCornerShape(cornerRadius)
    ) {

        Box(
            modifier = Modifier
                .fillMaxWidth()
                .background(
                    brush = Brush.horizontalGradient(colors = gradientColors),
                    shape = roundedCornerShape
                )
                .clip(roundedCornerShape)
                /*.background(
                    brush = Brush.linearGradient(colors = gradientColors),
                    shape = RoundedCornerShape(cornerRadius)
                )*/
                .padding(horizontal = 16.dp, vertical = 8.dp),
            contentAlignment = Alignment.Center
        ) {
            Text(
                text = nameButton,
                fontSize = 20.sp,
                color = Color.White
            )
        }
    }
}

2 Comments

Please add some comments like which part is the key to make it fullscreen.
If I understand it correctly, there are two parts making it full-screen. The first one is to set usePlatformDefaultWidth = false for DialogProperties. The second one is to have Modifier.fillMaxSize() for Surface.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.