Я попытался создать полноэкранный диалог с помощью Jetpack Compose, используя этот код:
Dialog(onDismissRequest = { /*TODO*/ }) {
NewPostDialog()
}
В итоге это выглядело примерно так. Как я могу удалить поле сбоку (отмечено красным)?
ОБНОВЛЕНИЕ: Как упомянул @Nestor Perez, начиная с compose 1.0.0-rc01, вы можете установить usePlatformDefaultWidth
in 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")
}
}
}
}
}
Я безуспешно пытался обновить AmbientContext с помощью ContextThemeWrapper. Возможно, вы можете использовать комбинацию фрагментов и компоновать там, где вы устанавливаете тему в макете фрагмента.
Высота не устанавливается на полную высоту экрана, некоторая часть нижней части показывает содержимое за диалоговым окном
Если вы хотите полностью отказаться от темы 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 в свойствах диалога, но все равно отображаются полосы с содержимым полностью внутри
Для меня работает идеально, не забудьте добавить Modifer.fillMaxHeight
.
PS: не передавайте MutableState
в качестве аргумента, используйте обратный вызов
Решение от 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()
}
}
У меня это плохо работает, увеличивается только внутреннее диалоговое пространство, но не само диалоговое окно. Таким образом, часть его становится скрытой.
UPD: Проблема только в режиме предварительного просмотра.
Чтобы создать полноэкранный диалог с помощью 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
)
}
}
}
Пожалуйста, добавьте несколько комментариев, например, какая часть является ключевой, чтобы сделать его полноэкранным.
Если я правильно понимаю, есть две части, делающие его полноэкранным. Первый — установить usePlatformDefaultWidth = false для DialogProperties. Второй — иметь Modifier.fillMaxSize() для Surface.
Если вы хотите, чтобы Jetpack создавал полноэкранный диалог, который покрывал весь экран, отображался под системными панелями (панель состояния и панель навигации) и поддерживал иммерсивный режим, который пока официально не поддерживается, но я нашел обходной путь:
Методы использования
// 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)
}
}
)
}
Это работает, спасибо! Есть ли способ применить тему только к конкретному диалоговому окну, а не ко всему приложению?