Полноэкранное диалоговое окно создания Jetpack

Я попытался создать полноэкранный диалог с помощью Jetpack Compose, используя этот код:

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

В итоге это выглядело примерно так. Как я могу удалить поле сбоку (отмечено красным)?

Полноэкранное диалоговое окно создания Jetpack

24
0
20 991
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

ОБНОВЛЕНИЕ: Как упомянул @Nestor Perez, начиная с compose 1.0.0-rc01, вы можете установить usePlatformDefaultWidthin DialogProperties, чтобы диалоговое окно заполнило всю ширину экрана:

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

Compose Dialog использует ContextThemeWrapper, поэтому вы сможете оформить свой диалог в собственном стиле.

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>

И в коде:

@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")
                }
            }
        }
    }
}

Это работает, спасибо! Есть ли способ применить тему только к конкретному диалоговому окну, а не ко всему приложению?

Yannick 19.12.2020 23:16

Я безуспешно пытался обновить AmbientContext с помощью ContextThemeWrapper. Возможно, вы можете использовать комбинацию фрагментов и компоновать там, где вы устанавливаете тему в макете фрагмента.

jns 22.12.2020 15:03

Высота не устанавливается на полную высоту экрана, некоторая часть нижней части показывает содержимое за диалоговым окном

Ali_Waris 07.01.2022 15:11

Если вы хотите полностью отказаться от темы xml, а также не делать этого для всех диалогов, вы можете установить модификатор requiredWidth равным LocalConfiguration.current.screenWidthDp.dp (умноженный на некоторую дробь, как вам угодно).

Пример, который занимает 0.96f ширины экрана:

@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
        }
    }
}

не работает, я даже установил для decorFitsSystemWindows значение false в свойствах диалога, но все равно отображаются полосы с содержимым полностью внутри

Shenanigans1 26.11.2022 14:27

Для меня работает идеально, не забудьте добавить Modifer.fillMaxHeight.

Arkadiusz Mądry 31.01.2023 20:51

PS: не передавайте MutableState в качестве аргумента, используйте обратный вызов

Peter 21.02.2023 16:07

Решение от jns не очень хорошо сработало для меня, я оставлю здесь другое решение, если кто-то все еще ищет:

Реализуйте тему как ответ jns:

<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>

Для диалога создайте каркас и добавьте экспериментальное свойство «usePlatformDefaultWidth = false» в свойства диалога:

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

У меня это плохо работает, увеличивается только внутреннее диалоговое пространство, но не само диалоговое окно. Таким образом, часть его становится скрытой.

Psijic 16.08.2022 14:59

UPD: Проблема только в режиме предварительного просмотра.

Psijic 16.08.2022 15:13

Чтобы создать полноэкранный диалог с помощью Jetpack Compose, используйте этот код: EG1: Скриншот полноэкранного диалога

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
            )
        }
    }
}

Пожалуйста, добавьте несколько комментариев, например, какая часть является ключевой, чтобы сделать его полноэкранным.

Arst 04.08.2022 02:04

Если я правильно понимаю, есть две части, делающие его полноэкранным. Первый — установить usePlatformDefaultWidth = false для DialogProperties. Второй — иметь Modifier.fillMaxSize() для Surface.

Howard S 21.12.2022 16:19

Если вы хотите, чтобы Jetpack создавал полноэкранный диалог, который покрывал весь экран, отображался под системными панелями (панель состояния и панель навигации) и поддерживал иммерсивный режим, который пока официально не поддерживается, но я нашел обходной путь:

  1. Найдите окно действия и диалоговое окно (это 2 разных окна) и примените флаги из окна действия к диалоговому окну, это позволит диалоговому окну рисоваться под системными панелями.
  2. Обновите параметры родительского макета в диалоговом окне, чтобы они соответствовали полноэкранному режиму.
  3. Сделайте системные панели прозрачными, чтобы содержимое диалогов было видно под ними.

Методы использования

// 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
    }

Полноэкранный диалог

@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()
            }
        }
    )
}

Полноэкранный диалог с навигацией

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)
            }
        }
    )
}

Другие вопросы по теме