I ran into an issue today where if I specify a trailing icon for a BasicTextField it forces the height of the field to be increased. Any way to override this setting?
As you can see in the image the 555g field is shorter than the Ingredient1 which became taller due to the addition of the trailingIcon.
I've tried to understand what's happening in compose and after digging I came across this code in the Google native TextField.kt
if (trailing != null) {
Box(
modifier = Modifier
.layoutId(LeadingId)
.then(IconDefaultSizeModifier),
contentAlignment = Alignment.Center
) {
trailing()
}
}
So I am inferring from this that IconDefaultSizeModifier is forcing the minHeight to be 48.dp
and below is the code that I use to create the BasicTextField
BasicTextField(
value = state,
//... more arguments
decorationBox = #Composable {
TextFieldDefaults.OutlinedTextFieldDecorationBox(
value = textVal,
visualTransformation = VisualTransformation.None,
innerTextField = it,
singleLine = singleLine,
enabled = enabled,
interactionSource = interactionSource,
trailingIcon = trailingIcon,
// keep vertical paddings but change the horizontal
contentPadding = TextFieldDefaults.textFieldWithoutLabelPadding(
top = 10.dp,
bottom = 10.dp,
start = 12.dp,
end = 8.dp
),
container = {}
)
}
)
You can apply the same height to both TextFields.
Something like:
BasicTextField(
modifier = Modifier.height(48.dp),
In the same way you can reduce the height.For example
BasicTextField(
modifier = Modifier.height(32.dp),
//... more arguments
decorationBox = #Composable {
TextFieldDefaults.OutlinedTextFieldDecorationBox(
contentPadding = TextFieldDefaults.textFieldWithoutLabelPadding(
top = 0.dp,
bottom = 10.dp,
start = 12.dp,
end = 0.dp
),
Related
This question already has answers here:
Jetpack Compose Decrease height of TextField
(5 answers)
Closed 4 months ago.
I'm trying to create a custom TextField using Android Jetpack compose, see below
TextFieldDefaults.OutlinedTextFieldDecorationBox(
contentPadding = PaddingValues(vertical = 10.dp),
value = searchText,
innerTextField = innerTextField,
enabled = enabled,
singleLine = singleLine,
visualTransformation = VisualTransformation.None,
interactionSource = interactionSource,
colors = textFieldColors,
leadingIcon = {
Icon(
modifier = Modifier.size(leadingIconSize),
painter = painterResource(id = R.drawable.ic_search),
contentDescription = ""
)
},
placeholder = {
Text("what is doin ")
}
)
The height of the text field should be 36dp due to design requirements, however the text field has its own height about 56dp.
I could not find any information how to customize that value so I used the parameter contentPadding to achieve the goal.
However, it seems too ugly for me. Is there any other way to achieve to implement this?
Thanks in advance.
Apply the height modifier to the BasicTextField.
Something like:
BasicTextField(
value = text,
onValueChange = { text = it },
modifier = Modifier
.height(36.dp),
singleLine = singleLine,
interactionSource = interactionSource
) { innerTextField ->
TextFieldDefaults.OutlinedTextFieldDecorationBox(
value = text,
innerTextField = innerTextField,
enabled = enabled,
singleLine = singleLine,
visualTransformation = VisualTransformation.None,
interactionSource = interactionSource,
contentPadding = TextFieldDefaults.textFieldWithoutLabelPadding(
top = 0.dp,
bottom = 0.dp
)
)
}
I wrote the composable below (shown as dialog). When viewState.errorCode != 0, another composable is shown. This all works fine, but the height of the box doesn't adjust when the new composable becomes visible.This results in an 'invisible overflow' whereby a number of items are no longer visible. Is there a way to make the box dynamic so that it adjusts in height when a new element becomes visible?
Box(modifier = Modifier
.clip(RoundedCornerShape(4.dp))) {
Column(
modifier = Modifier
.background(MaterialTheme.colors.onPrimary, MaterialTheme.shapes.large)
.padding(12.dp)
) {
Text(stringResource(R.string.verify_hint, user.email).parseBold(), fontSize = 18.textDp, fontFamily = SourceSans)
if (viewState.errorCode != 0) {
AlertMessage(message = stringResource(id = viewState.errorCode), color = errorColor, padding = PaddingValues(top = 12.dp))
}
TextField(
value = code,
onValueChange = { code = it },
label = { Text(stringResource(R.string.verification_code)) },
colors = TextFieldDefaults.textFieldColors(backgroundColor = textFieldColor),
singleLine = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier
.fillMaxWidth()
.padding(top = 12.dp, bottom = 12.dp)
)
NMButton(
onClick = { viewModel.verify(user, code, verifyLogin, language = context.getLanguageBasedOnConfiguration()) },
modifier = Modifier
.fillMaxWidth()
.padding(start = 0.dp, end = 0.dp),
icon = R.drawable.ic_badge_check_solid,
label = stringResource(R.string.verify)
)
}
if (viewState.loading) {
Loader()
}
}
Yes there is.
You can use animateContentSize on your box modifier
then you declare an animationSpec like below:
Box(modifier = Modifier
.animateContentSize(
animationSpec = tween(
durationMillis = 300,
easing = LinearOutSlowInEasing
)
).clip(RoundedCornerShape(4.dp))
)
Now if the AlertMessage appears it will animate the size of the box.
You can also create expandable cards with this approach.
This video may help you too.
I have a problem with RadioButton component in my Jetpack Compose application. I have some RadioButtons with text and this have a lot of padding by default. Can I remove this padding or to set a custom padding to avoid a lot of space between each?
Currently I have this:
My code is:
Column {
MyEnum.values().filter { rb -> rb.visible }.forEach { rb ->
Row(
Modifier
.fillMaxWidth()
.padding(horizontal = 0.dp, vertical = 0.dp)
.clickable(
interactionSource = interactionSource,
indication = null
) {
TODO()
},
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = (rb.position == selectedOption),
onClick = {
TODO()
},
colors = RadioButtonDefaults.colors(
selectedColor = DialogOutlinedTextFocus,
unselectedColor = DialogOutlinedTextUnfocus
)
)
Text(
text = stringResource(id = rb.idText),
color = Color.Black,
fontSize = 14.sp,
modifier = Modifier
.padding(horizontal = 3.dp, vertical = 2.dp)
)
}
}
}
I tried with contentPadding, but this property does not exist in RadioButton component.
The source code for RadioButton.kt sets the padding modifier at line 108. With modifiers, order matters. Even if you provide your own padding modifier it will be overridden by line 108.
The sizing values are hardcoded at the bottom of the file.
If you really want to "reduce the padding", apply a size modifier. I use 20.dp because it's equal to radioButtonSize in RadioButton.kt so the button doesn't get clipped. This should work for your purposes since the entire row is clickable.
RadioButton(
modifier = Modifier.size(20.dp),
selected = selected,
onClick = { TODO() },
)
Although, you're probably better off in the long term making custom components you can control. Luckily, the source code is ready available. Just copy, paste and adjust to your needs.
You could specify the Row height in the Row.Modifier
like this:
Row(
Modifier
.fillMaxWidth()
//HERE YOU GO
.height(30.dp)
.padding(horizontal = 0.dp, vertical = 0.dp)
My PersonalDataBox() doesn't fill max available space even if there is .fillMaxSize()
There is a empty space between my box and the bottom of the screen. I want to fill my whole screen with Box(). It looks like my box has .fillWrapHeight(). Parent of the box is also .fillMaxSize() and it works correctly - fills max height.
#Composable
private fun PersonalDataBox(user: User) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 10.dp)
.clip(RoundedCornerShape(topStart = 10.dp, topEnd = 10.dp))
.background(Color.Red)
.padding(horizontal = 25.dp, vertical = 15.dp)
) {
Column(modifier = Modifier.fillMaxSize()) {
Text(
text = stringResource(R.string.personal_data),
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
color = MaterialTheme.colors.primary
)
listOf(
Pair(stringResource(R.string.name), user.name),
Pair(stringResource(R.string.surname), user.surname),
Pair(stringResource(R.string.e_mail), user.email),
).forEach {
InputField(it)
}
}
}
}
I pulled down your code and it actually renders in the whole screen just fine--so I'm assuming you have your PersonalDatabox in a compose view with infinite height such as a LazyColumn.
Check out the documentation about constraints here
Specifically:
Most commonly, when measured with unbounded Constraints, these children will fallback to size themselves to wrap their content, instead of expanding to fill the available space (this is not always true as it depends on the child layout model, but is a common behavior for core layout components).
In other words, if the constraint is infinite, then .fillMaxSize will default to wrapping content.
To get around this, you can make the constraint equal to the height of the Lazy Column by using fillParentMaxHeight.
This only works if you're using a LazyColumn, though. Otherwise, if you set the height specifically or measure the screen you can accomplish the same thing.
Here's an example of what that might look like.
LazyColumn() {
item {
Column(modifier = Modifier.fillParentMaxHeight(1f)) {
PersonalDataBox(User(name = "John", surname = "Robless", "coolemail#email.com"))
}
}
}
Your Box has the following modifiers:
modifier = Modifier
.fillMaxSize()
.padding(vertical = 10.dp)
.clip(RoundedCornerShape(topStart = 10.dp, topEnd = 10.dp))
.background(Color.Red)
.padding(horizontal = 25.dp, vertical = 15.dp)
The important ones are
.padding(vertical = 10.dp)
.padding(horizontal = 25.dp, vertical = 15.dp)
There are two padding modifiers adding a total of 25.dp padding to the top and bottom of your Box, remove these and it should fix your issue
I have two layers in my Compose layout: One for the actual content and the one above is a Box-wrapped OutlinedTextField for search queries.
Here's the sample code:
// Placeholder for layout content
Box(modifier = Modifier.fillMaxSize()) {
Text(
text = stringResource(id = R.string.lorem_ipsum),
color = Color.Gray
)
}
// Overlaying Box with OutlinedTextField
Box(modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp, top = 64.dp)
) {
OutlinedTextField(
value = viewModel.query.value,
onValueChange = { viewModel.updateQuery(it) },
modifier = Modifier
.fillMaxWidth()
.align(Alignment.BottomCenter)
.navigationBarsWithImePadding(),
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Characters,
autoCorrect = false,
keyboardType = KeyboardType.Text,
imeAction = ImeAction.None
),
shape = CircleShape,
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.White
),
placeholder = { Text(stringResource(id = R.string.search_input_placeholder)) },
maxLines = 1,
singleLine = true
)
}
Even though I'm setting
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.White
)
in the OutlinedTextField, the background stays transparent as you can see in the following screenshot:
Adding a background color on the OutlinedTextField's modifier is giving the whole row a rectangled background, which is also not the desired result.
The OutlinedTextField with it's CircleShape should only have a background inside of the shape. How can I achieve that?
I'm using Jetpack Compose version 1.0.4.
Reason
Unfortunately, in version 1.0.4, OutlinedTextField ignores the backgroundColor even if you specify it, so you can remove the colors parameter.
Support for it will be added in 1.1.0:
https://android-review.googlesource.com/c/platform/frameworks/support/+/1741240
It's already present in 1.1.0-alpha06 so you can play with it if you want. Your original code should produce desired outcome on that version.
Solution
To achieve what you want (before 1.1.0 is released) you can simply add a background modifier with the same shape:
.background(Color.White, CircleShape)
Since the order of modifiers is important, you should add it after you apply all the paddings (navigationBarsWithImePadding in your case). Like that:
modifier = Modifier
.fillMaxWidth()
.align(Alignment.BottomCenter)
.navigationBarsWithImePadding()
.background(Color.White, CircleShape),
Note: Also be aware that you cannot use the label parameter with this approach, because the TextField with label will be higher than just the outline shape and these two shaped wouldn't match in size anymore.
Result
Entire code:
// Placeholder for layout content
Box(modifier = Modifier.fillMaxSize()) {
Text(
text = stringResource(id = R.string.lorem_ipsum),
color = Color.Gray
)
}
// Overlaying Box with OutlinedTextField
Box(modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp, top = 64.dp)
) {
OutlinedTextField(
value = viewModel.query.value,
onValueChange = { viewModel.updateQuery(it) },
modifier = Modifier
.fillMaxWidth()
.align(Alignment.BottomCenter)
.navigationBarsWithImePadding()
.background(Color.White, CircleShape),
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Characters,
autoCorrect = false,
keyboardType = KeyboardType.Text,
imeAction = ImeAction.None
),
shape = CircleShape,
placeholder = { Text(stringResource(id = R.string.search_input_placeholder)) },
maxLines = 1,
singleLine = true
)
}
How the final result looks like: