It seems impossible to change the width of the dialog with Compose.
The closest I've come to with changing the dialog width is through DialogProperties.usePlatformDefaultWidth. Setting it to false causes the dialog to fill the screen but is there a way to use custom width?
Use can define a custom AlertDialog using the constructor with text,title and buttons parameters and applying a size (for example with the Modifier.size) and overriding the default behaviour with usePlatformDefaultWidth = false :
AlertDialog(
onDismissRequest = { /*TODO*/ },
title = {
Text(text = "Title")
},
text = {
Text(
"This area typically contains the supportive text " +
"which presents the details regarding the Dialog's purpose."
)
},
buttons = {},
properties = DialogProperties(
usePlatformDefaultWidth = false
),
modifier = Modifier.size(200.dp,250.dp)
)
If you want to use a constant width in all your project you can create a dialog with customized width as follows
#Composable
fun MyCustomDialog(
onDismissRequest: () -> Unit,
properties: DialogProperties = DialogProperties(),
content: #Composable () -> Unit
) {
Dialog(
onDismissRequest = onDismissRequest,
// We are copying the passed properties
// then setting usePlatformDefaultWidth to false
properties = properties.let {
DialogProperties(
dismissOnBackPress = it.dismissOnBackPress,
dismissOnClickOutside = it.dismissOnClickOutside,
securePolicy = it.securePolicy,
usePlatformDefaultWidth = false
)
},
content = {
Surface(
color = Color.Transparent,
modifier = Modifier.width(250.dp), // Customize your width here
content = content
)
}
)
}
Related
On my Samsung Galaxy S22+ with One UI 5.0 and Android 13, compose AlertDialog always takes up full width, on other devices it works just as expected.
Compose version is 1.3.1
You can reproduce this by simply just downloading material catalog app from Google Play store.
I suspect this is most likely a bug on Compose side, if there's a quick fix, I'd appreciate it.
#Composable
fun AlertDialogSample() {
val openDialog = remember { mutableStateOf(true) }
if (openDialog.value) {
AlertDialog(
onDismissRequest = {
// Dismiss the dialog when the user clicks outside the dialog or on the back
// button. If you want to disable that functionality, simply use an empty
// onCloseRequest.
openDialog.value = false
},
title = {
Text(text = "Title")
},
text = {
Text(
"This area typically contains the supportive text " +
"which presents the details regarding the Dialog's purpose."
)
},
confirmButton = {
TextButton(
onClick = {
openDialog.value = false
}
) {
Text("Confirm")
}
},
dismissButton = {
TextButton(
onClick = {
openDialog.value = false
}
) {
Text("Dismiss")
}
}
)
}
}
After Android 13 update on my device, Dialog with XML layouts are taking expected width. But Compose AlertDialog & Dialog are taking up full width. We are facing this issue with Compose Dialogs only,
I am using Samsung Galaxy M32 with One UI 5.0 & Android 13, App uses Compose version 1.1.0-beta01 & targetSdkVersion 33,
using usePlatformDefaultWidth = true did not help,
This issue is most likely a bug on Compose side,
You can find quick fixes for both Dialog and AlertDialog in compose,
For Compose AlertDialog()
I have used modifier and set DialogProperty usePlatformDefaultWidth to false & set fillMaxWidth with fraction 0.92f.
modifier = Modifier.fillMaxWidth(0.92f),
properties =DialogProperties(usePlatformDefaultWidth =false),
Compose AlertDialog() code snippet:
AlertDialog(
modifier = Modifier.fillMaxWidth(0.92f),
properties = DialogProperties(
usePlatformDefaultWidth = false
),
onDismissRequest = { ... },
buttons = {
Column(
modifier = Modifier
.fillMaxWidth()
) {
...
}
},
title = {
},
text = {
Column(
modifier = Modifier
.fillMaxWidth()
) {
........
}
}
)
For Compose Dialog()
I have used Surface to wrap the dialog content with modifier = Modifier.fillMaxWidth(0.92f),
RoundedCornerShape with radius, set Color.Transparent to background color and also set DialogProperty usePlatformDefaultWidth to false
Surface(
modifier = Modifier.fillMaxWidth(0.92f),
shape = RoundedCornerShape(8.dp),
color = Color.Transparent,
content = {})
Compose Dialog() code snippet:
Dialog(
onDismissRequest = { },
properties = DialogProperties(
dismissOnClickOutside = true,
dismissOnBackPress = true,
usePlatformDefaultWidth = false
),
content = {
Surface(
modifier = Modifier.fillMaxWidth(0.92f),
shape = RoundedCornerShape(8.dp),
color = Color.Transparent,
content = {
Column(
modifier = Modifier
.background(color = colorResource(id = android.R.color.white))
.fillMaxWidth(1f)
.wrapContentHeight(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
........
}
})
})
The Alert-Dialog-Composable accepts DialogProperties
#Composable
fun AlertDialog(
properties: DialogProperties = DialogProperties()
...
)
/**
* Properties used to customize the behavior of a [Dialog].
...
* #property usePlatformDefaultWidth Whether the width of the dialog's content should
* be limited to the platform default, which is smaller than the screen width.
*/
class DialogProperties #ExperimentalComposeUiApi constructor(
val usePlatformDefaultWidth: Boolean = true
...
)
By default, usePlatformDefaultWidth = true, so the Dialog should not fill the screen width.
-> What you see is most probably a bug & should be reported
The issue I faced was that I needed AlertDialog with some kind of List items (e. g. LazyColumn) and the TextField to search across these items. I wanted to display all the Dialog layout even when Keyboard is opened. But what I got is a Keyboard that cover some part of Dialog layout itself. I tried to use imePadding() for Dialog's Modifier but seems that Dialog ignoring that. I didn't find any solution for this on the Internet.
My code looks like so:
AlertDialog(
modifier = Modifier.fillMaxWidth()
.padding(AppTheme.margins.edge)
.imePadding(),
onDismissRequest = {
searchText = TextFieldValue("")
viewModel.clearSearchQuery()
dismissCallback?.invoke()
},
text = {
Column(
modifier = Modifier.wrapContentHeight()
) {
Text(
text = stringResource(R.string.dlg_select_content_title),
style = AppTheme.textStyles.hugeTitleText
)
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(top = AppTheme.margins.divRegular),
value = searchText,
placeholderText = stringResource(R.string.dlg_select_content_search_placeholder),
onValueChange = { newValue ->
searchText = newValue
viewModel.onSearchTextTyped(newValue.text)
}
)
RadioGroup(
modifier = Modifier
.verticalScroll(rememberScrollState()),
options = labels.map {
RadioOption(
title = it.name,
description = null,
selected = vmState.selectedLabel?.id == it.id,
tag = it.id
)
},
onOptionSelected = {
searchText = TextFieldValue("")
viewModel.clearSearchQuery()
viewModel.saveLabelSelection(it.tag as Int) {
dismissCallback?.invoke()
}
}
)
}
},
properties = DialogProperties(
usePlatformDefaultWidth = false
),
confirmButton = {
// Nothing
}
)
And the result:
I am not able to interact with several last items in list because Keyboard covers it.
I have implemented a solution for this issue. The solution is quite ugly, but working. If someone knows a more elegant solution, feel free to write it in an answer in this question.
Even though the dialog ignores the imePadding() we still can set the height. So, first of all we should to know what screen height available above keyboard.
#Composable
private fun TrickyHeight(
onHeightChanged: (Dp) -> Unit,
) {
val density = LocalDensity.current
Box(
modifier = Modifier
.fillMaxSize()
.imePadding()
.padding(bottom = 30.dp) // additional padding
.onSizeChanged {
onHeightChanged.invoke(with(density) { it.height.toDp() })
}
)
}
Next step is to create wrapper over AlertDialog:
#Composable
fun TrickyDialog(
onDismissRequest: () -> Unit,
confirmButton: #Composable () -> Unit,
dismissButton: #Composable (() -> Unit)? = null,
icon: #Composable (() -> Unit)? = null,
title: #Composable (() -> Unit)? = null,
text: #Composable (() -> Unit)? = null,
shape: Shape = AlertDialogDefaults.shape,
containerColor: Color = AppTheme.colors.surfaceColor,
iconContentColor: Color = AlertDialogDefaults.iconContentColor,
titleContentColor: Color = AlertDialogDefaults.titleContentColor,
textContentColor: Color = AlertDialogDefaults.textContentColor,
tonalElevation: Dp = AlertDialogDefaults.TonalElevation,
properties: DialogProperties = DialogProperties()
) {
val maxDialogHeight = remember { mutableStateOf(0.dp) }
TrickyHeight(onHeightChanged = { maxDialogHeight.value = it })
AlertDialog(
modifier = Modifier
.fillMaxWidth()
.heightIn(0.dp, maxDialogHeight.value)
.padding(AppTheme.margins.edge),
onDismissRequest = onDismissRequest,
confirmButton = confirmButton,
dismissButton = dismissButton,
icon = icon,
title = title,
text = text,
shape = shape,
containerColor = containerColor,
iconContentColor = iconContentColor,
titleContentColor = titleContentColor,
textContentColor = textContentColor,
tonalElevation = tonalElevation,
properties = properties
)
}
Also, do not forget to add correct android:windowSoftInputMode in Manifest: android:windowSoftInputMode="adjustResize"
Now you can use TrickyDialog instead of AlertDialog. Again, this solution is not elegant. But maybe it will be helpful for someone who faced the same issue. Also, this solution will not work properly for Landscape Screen Orientation.
As of Compose UI 1.3.0-beta01, you can set DialogProperties.decorFitsSystemWindows to false and imePadding() will work.
https://issuetracker.google.com/issues/229378542
https://developer.android.com/jetpack/androidx/releases/compose-ui#1.3.0-beta01
AlertDialog(
modifier = Modifier.imePadding(),
properties = DialogProperties(decorFitsSystemWindows = false),
onDismissRequest = {
// ...
},
title = {
// ...
},
text = {
// ...
},
confirmButton = {
// ..
},
)
The Code A displays a dialog box based AlertDialog, and I get Image A when I run Code A.
I find the space between title = { Text(text = dialogTitle) } and text = {...} is too closer in Image A.
So I set Modifier.padding(top = 100.dp) to wish to increase the space between the two controls, but I only get Image B, it seems that Modifier.padding(top = 100.dp) doesn't work as expected, how can I fix it?
Code A
#Composable
fun EditTextDialog(
isShow: Boolean,
onDismiss: () -> Unit,
onConfirm: (String) -> Unit,
saveTitle: String = stringResource(R.string.dialog_save_title),
cancelTitle:String = stringResource(R.string.dialog_cancel_title),
dialogTitle:String ="Edit",
editFieldContent:String ="",
) {
var mText by remember(editFieldContent){ mutableStateOf(editFieldContent) }
val cleanAndDismiss = {
mText = editFieldContent
onDismiss()
}
if (isShow) {
AlertDialog(
title = { Text(text = dialogTitle) },
text = {
Column(
Modifier.padding(top = 20.dp)
//Modifier.padding(top = 100.dp)
//Modifier.height(100.dp), //The same result as Image A
//verticalArrangement = Arrangement.Center
) {
TextField(
value = mText,
onValueChange = { mText = it }
)
}
},
confirmButton = {
TextButton(onClick = { onConfirm(mText) }) {
Text(text = saveTitle)
}
},
dismissButton = {
TextButton(onClick = cleanAndDismiss) {
Text(text = cancelTitle)
}
},
onDismissRequest = cleanAndDismiss
)
}
}
Image A
Image B
With M3 AlertDialog (androidx.compose.material3.AlertDialog) it works.
With M2 AlertDialog, one solution is to remove the title attribute and use the text attribute for the whole layout.
AlertDialog(
onDismissRequest = {},
text = {
Column(){
Text(text = "Title")
Spacer(Modifier.height(30.dp))
TextField(
value = "mText",
onValueChange = { },
)
}
},
//buttons..
)
I don't understand what you're trying to do. If you want more space between the TextField and the dialog buttons, then you don't want a top padding. You want padding below the TextField, so it would be bottom padding on the column.
Also, there's a chance that it won't work properly inside a Column, and you might have to switch it out for Box. And if that doesn't work for some reason, just add a spacer below the TextField:
Spacer(Modifier.height(20.dp).fillMaxWidth())
I assume you are using Material AlertDialog? If yes try using the Material3 variant. It should work then.
Just implement following library:
implementation "androidx.compose.material3:material3:1.0.0-beta02"
And make sure to use the Material3 AlertDialog Composable which is imported with the library.
In a simple AlertDialog like the following
AlertDialog(
modifier = Modifier,
title = {
Text(text = "Title")
},
text = {
Column(
modifier = Modifier.fillMaxWidth()
) {
TextButton() {
Text("Text 1")
}
TextButton() {
Text("Text 2")
}
}
},
confirmButton = {},
dismissButton = {}
)
how can I set a spacing between title and the first TextButton?
I tried to set a .padding(top = X.dp) to the Column, or the first text button, but this seems to only create a space at the bottom of the AlertDialog.
Also setting a custom .height(X.dp) did not work.
I'm using Compose 1.0.3
As #Abhimanyu perfectly explains why it's not working right now, here's the workaround I'm using to achieve the desired padding: putting the title in the content. AlertDialog's title param is optional, so it can be omitted/set to null, and instead the actual title can be put in the text parameter (which holds the dialog content).
#Composable
fun MyComposable() {
AlertDialog(
title = null,
text = {
Column {
Text(
modifier = Modifier.padding(vertical = 16.dp),
text = "Actual title"
)
// Rest of the dialog content
}
}
)
}
This is NOT an answer. It only provides info on why this is not possible.
The requirement seems not achievable at this point (6th Oct 2021) with the current compose version (1.0.3).
Will update this once that is possible.
The AlertDialog code does not respect the padding values provided.
AlertDialog.kt
// Baseline distance from the first line of the text to the last line of the title
private val TextBaselineDistanceFromTitle = 36.sp
The text offset used for the positioning is calculated like this.
val textOffset = if (titlePlaceable == null) {
TextBaselineDistanceFromTop.roundToPx()
} else {
TextBaselineDistanceFromTitle.roundToPx()
}
The distance between the first text in the text composable and the last text in the title composable is always 36.sp.
The Alert Dialog code in compose seems too hackish currently and I could see a few TODO's in the code.
Hopefully, the code will be changed to handle more scenarios soon.
I'm using this composable as first child inside Column
#Composable
fun HackySpacer(space: Dp) {
Box(
modifier = Modifier
.height(space)
.fillMaxWidth()
) {
Text(text = "")
}
}
It's not perfect, but it works for my usecase.
Is now possible using the new AlertDialog from Compose Material 3.
The default spacing between title and text is much more reasonable and it is also possible to add Modifier.padding() or Spacer() to both.
implementation("androidx.compose.material3:material3:1.0.0-alpha01")
androidx.compose.material3.AlertDialog(
onDismissRequest = {
openDialog.value = false
},
title = {
Text(text = "Title", modifier = Modifier.padding(50.dp))
},
text = {
Spacer(Modifier.height(50.dp))
Text(text = "Turned on by default")
},
confirmButton = {
TextButton(
onClick = {
openDialog.value = false
}
) {
Text("Confirm")
}
},
dismissButton = {
TextButton(
onClick = {
openDialog.value = false
}
) {
Text("Dismiss")
}
}
)
Is there any way to change slider thumb size? I think for now we can only manipulate colors
var sliderPosition by remember { mutableStateOf(0f) }
Text(text = sliderPosition.toString())
Slider(
value = sliderPosition,
onValueChange = { sliderPosition = it },
valueRange = 0f..100f,
onValueChangeFinished = {
// launch some business logic update with the state you hold
// viewModel.updateSelectedSliderValue(sliderPosition)
},
steps = 5,
colors = SliderDefaults.colors(
thumbColor = MaterialTheme.colors.secondary,
activeTrackColor = MaterialTheme.colors.secondary
)
)
No, this size cannot be modified. The only thing you can do is copy the entire Slider.kt file into your project and modify it.
It is a good idea to give the new view a different name to avoid misunderstandings in the future.
You should change ThumbRadiusconstant, or make it a variable if you need different sizes in your application.
With M3 androidx.compose.material3.Slider you can use the thumb attribute to customize the size.
Something like:
var sliderPosition by remember { mutableStateOf(0f) }
Column {
Text(text = sliderPosition.toString())
Slider(
modifier = Modifier.semantics { contentDescription = "Localized Description" },
value = sliderPosition,
onValueChange = { sliderPosition = it },
valueRange = 0f..5f,
steps = 4,
interactionSource = interactionSource,
onValueChangeFinished = {
// launch some business logic update with the state you hold
},
thumb = {
SliderDefaults.Thumb( //androidx.compose.material3.SliderDefaults
interactionSource = interactionSource,
thumbSize = DpSize(40.dp,40.dp)
)
},
)
}
Note: it requires for material3 at least the version 1.0.0-beta03
I've created a library for easy customization of Slider, since Slider from Material package is not flexible.
https://github.com/krottv/compose-sliders. Below is the code example of how to use it to make thumb size smaller:
var stateSlider by remember { mutableStateOf(0.5f) }
SliderValueHorizontal(
stateSlider, { stateSlider = it },
modifier = Modifier
.fillMaxWidth(),
// desired size of Slider's thumb
thumbSize = DpSize(8.dp, 8.dp)
)
Also you can specify custom composables for thumb and track.
Yes, but only wrapping it with AndroidView and wait for the better future, when Google team release another update in Material lib.
Here is an example
AndroidView(
modifier = Modifier...//,
factory = { context ->
Slider(
ContextThemeWrapper(context, context.theme)
).apply {
// set listeners
it.addOnSliderTouchListener(object : SliderView.OnSliderTouchListener {
#SuppressLint("RestrictedApi")
override fun onStartTrackingTouch(slider: Slider) = Unit
#SuppressLint("RestrictedApi")
override fun onStopTrackingTouch(slider: Slider) {
onValueChangeFinished.invoke()
}
})
it.addOnChangeListener { _, value, _ ->
onValueChanged.invoke(value)
}
// your thumb customization
// your track customization
}
}, update = {
// set value
it.value = currentValue
})
Should be placed inside #Composable
AndroidView in Compose
Slider in Material