26

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

Yannick
  • 4,833
  • 8
  • 38
  • 63

5 Answers5

66

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")
                }
            }
        }
    }
}
jns
  • 6,017
  • 2
  • 23
  • 28
  • 3
    It works, Thanks! Is there a way to only apply the theme to a particular dialog instead of the whole app? – Yannick Dec 19 '20 at 22:16
  • 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 – jns Dec 22 '20 at 14:03
  • 6
    Height is not getting set to full screen height, some part of bottom section shows the content behind the dialog – Ali_Waris Jan 07 '22 at 14:11
  • There is an issue in the android issue tracker to set a theme for a particular dialog: https://issuetracker.google.com/issues/198064319 – josias May 25 '23 at 15:29
5

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()
    }
}
Nestor Perez
  • 827
  • 11
  • 17
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
        }
    }
}
Can_of_awe
  • 1,431
  • 1
  • 11
  • 17
4

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)
            }
        }
    )
}
doolle89
  • 171
  • 1
  • 5
0

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
            )
        }
    }
}
  • 1
    Please add some comments like which part is the key to make it fullscreen. – Arst Aug 04 '22 at 00:04
  • 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. – Howard S Dec 21 '22 at 15:19