I have an OutlinedTextField with label text. When the field is empty, I want the label hint aligned to the end of the field, like this: .
However, the only way I've found to do this is by expanding the text to its maximum width so that the alignment has an effect. However, when the text field has some value and the label gets moved to the top border, the border is interrupted because the label is at its max width (the border should extend all the way to the 'F'):
I've tried a couple different things:
- Setting the width of the Text composable to maximum available (border is still interrupted when field has value)
- Setting the width of the Text composable to the intrinsic max (Text composable stays the same width and is not aligned correctly).
- Wrapping the Text in a Box, making the box as wide as possible and using the
contentAlign
parameter of the Box (same as 1)
Is there any way to align the label without affecting the border? The code below results in incorrect alignment but correct border behavior:
@Composable
fun TextFieldLabel(text: String, modifier: Modifier = Modifier) {
// Wrapping in a box has the same behavior as adding .fillMaxWidth() to the Text modifier
// Box(modifier = modifier.fillMaxWidth(), contentAlignment = Alignment.CenterEnd) {
Text(
text = text,
textAlign = TextAlign.End,
modifier = modifier // adding .fillMaxWidth() will correct the alignment but cause border to be cut short
// adding .width(IntrinsicSize.Max) gives incorrect alignment but border extends all the way
)
// }
}
@Composable
fun CurrencyInputTextField(
modifier: Modifier = Modifier
) {
OutlinedTextField(
value = "",
label = { TextFieldLabel(label) },
prefix = {Text(text = "$")},
onValueChange = {},
singleLine = true,
textStyle = MaterialTheme.typography.bodyLarge.copy(textAlign = TextAlign.End),
modifier = modifier
)
}
I have an OutlinedTextField with label text. When the field is empty, I want the label hint aligned to the end of the field, like this: .
However, the only way I've found to do this is by expanding the text to its maximum width so that the alignment has an effect. However, when the text field has some value and the label gets moved to the top border, the border is interrupted because the label is at its max width (the border should extend all the way to the 'F'):
I've tried a couple different things:
- Setting the width of the Text composable to maximum available (border is still interrupted when field has value)
- Setting the width of the Text composable to the intrinsic max (Text composable stays the same width and is not aligned correctly).
- Wrapping the Text in a Box, making the box as wide as possible and using the
contentAlign
parameter of the Box (same as 1)
Is there any way to align the label without affecting the border? The code below results in incorrect alignment but correct border behavior:
@Composable
fun TextFieldLabel(text: String, modifier: Modifier = Modifier) {
// Wrapping in a box has the same behavior as adding .fillMaxWidth() to the Text modifier
// Box(modifier = modifier.fillMaxWidth(), contentAlignment = Alignment.CenterEnd) {
Text(
text = text,
textAlign = TextAlign.End,
modifier = modifier // adding .fillMaxWidth() will correct the alignment but cause border to be cut short
// adding .width(IntrinsicSize.Max) gives incorrect alignment but border extends all the way
)
// }
}
@Composable
fun CurrencyInputTextField(
modifier: Modifier = Modifier
) {
OutlinedTextField(
value = "",
label = { TextFieldLabel(label) },
prefix = {Text(text = "$")},
onValueChange = {},
singleLine = true,
textStyle = MaterialTheme.typography.bodyLarge.copy(textAlign = TextAlign.End),
modifier = modifier
)
}
Share
Improve this question
asked Mar 26 at 10:45
theasianpianisttheasianpianist
4316 silver badges17 bronze badges
1 Answer
Reset to default 0Maybe this can help, what if you add a top line just to complete the space?
You add a onChangeSize listener, just to know the label spacing and use it as padding for the top line
@Composable
fun TextFieldLabel(
text: String,
modifier: Modifier = Modifier,
onChangeSize: (Dp)->Unit,
) {
Box(
modifier = modifier
.fillMaxWidth(),
) {
val density = LocalDensity.current
Text(
text = text,
textAlign = (TextAlign.End),
modifier = Modifier
.align(Alignment.CenterEnd)
.requiredWidth(IntrinsicSize.Min)
, onTextLayout = {
val width = it.size.width
if (width > 0) {
val textWidthDp = with(density) { width.toDp() }
onChangeSize(textWidthDp)
}
}
)
}
}
And then just handle the horizontal line:
@Composable
fun CurrencyInputTextField(
modifier: Modifier = Modifier,
label: String
) {
var value by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
var isFocused by remember { mutableStateOf(false) }
var labelWidth by remember { mutableStateOf(0.dp) }
Box (
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 2.dp)
) {
Box(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 2.dp),
) {
val showTopLine = (isFocused || value.isNotBlank())
HorizontalDivider(
thickness = if(isFocused)
OutlinedTextFieldDefaults.FocusedBorderThickness else
OutlinedTextFieldDefaults.UnfocusedBorderThickness,
color = if(isFocused)
OutlinedTextFieldDefaults.colors().focusedLabelColor
else OutlinedTextFieldDefaults.colors().unfocusedLabelColor,
modifier = Modifier
.padding(horizontal = 3.dp)
.padding(top = 8.1.dp)
.alpha(if(showTopLine) 1f else 0f)
.fillMaxWidth()
.padding(end = labelWidth + 15.dp)
)
}
OutlinedTextField(
value = value,
label = {
TextFieldLabel(label, onChangeSize = {
labelWidth = it
}) },
prefix = { Text(text = "$") },
onValueChange = {
value = it
},
singleLine = true,
textStyle = MaterialTheme.typography.bodyLarge.copy(textAlign = TextAlign.End),
modifier = modifier
.focusRequester(focusRequester)
.onFocusChanged { focusState ->
isFocused = focusState.isFocused
}
)
}
}
Maybe you will need to adjust some padding or color or the thickness, but this can give you an idea how can you make it.
I'm storing the label width dp in labelWidth
and this is used as end padding for the horizontal line.