Reputation: 665
Is there any way to put OutlinedTextField's label on top (expanded) and still have the placeholder, when OutlinedTextField is not in focus?
In xml layouts we have TextInputLayout that has expandedHintEnabled attribute that does the expanding.
Upvotes: 2
Views: 1950
Reputation: 105
I just wrestled with this and came up with a fairly easy and elegant solution, assuming you're controlling the value of the OutlinedTextField from a viewmodel...
var isFocused by rememberSaveable { mutableStateOf(false) }
OutlinedTextField(
value = if (viewmodel.stringvalue.isEmpty() && !isFocused) {
"your placeholder text" } else {
viewmodel.stringvalue },
label = { "your label" },
modifier = Modifier
.onFocusChanged { isFocused = it.isFocused },
placeholder = { Text(text ="your placeholder text") },
// any other parameters
With this, if you don't want to use a placeholder, simply change "your placeholder text" to a space (" "), (you must have at least a space).
The OutlinedTextField will place the label in the upper position whenever it is focused or whenever the value parameter is not an empty string. Even if you set a placeholder, the label will force it's way in.
So what we do is monitor the focused state and if the OutlinedTextField is not in focus and the value is blank, we replace the value with the placeholder. This creates a value in the field, forcing the label into the upper position.
When a user selects the field, it's focus state changes and the label moves up and the field is now actually blank (the non-blank value is set only if the field is not focused and is empty), but because the field is blank, the placeholder you place in the "value" parameter must also be placed in the "placeholder" parameter (if you're using a placeholder, if you don't want a place holder and are passing a space, you don't need to define the placeholder parameter).
And if for instance you're making the placeholder text have some other parameters like a particular style or fontweight or something, you can apply that to the fake "placeholder" in the value parameter by setting the OutlineTextField's "textStyle" parameter in the same way as the value:
textStyle = if (viewmodel.stringvalue.isEmpty() && !isFocused) {
LocalTextStyle.current.copy(
// placeholder text style parameters
) } else { LocalTextStyle.current }
And if you've got more than one field you're doing this with, each one will need a separate var = isFocused (isFocused1, isFocused2, etc).
Upvotes: 0
Reputation: 364730
Currently the placeholder
applies an alpha
modifier with this condition InputPhase.UnfocusedEmpty -> if (showLabel) 0f else 1f
and there isn't a parameter to achieve the same behaviour of expandedHintEnabled
.
A workaround can be to use a visualTransformation
to display a placeholder when the text is empty, removing the placeholder
parameter.
Something like:
val textColor = if (text.isEmpty())
MaterialTheme.colors.onSurface.copy(ContentAlpha.medium)
else
LocalContentColor.current.copy(LocalContentAlpha.current)
val textStyle = if (text.isEmpty())
LocalTextStyle.current.merge(MaterialTheme.typography.subtitle1)
else
LocalTextStyle.current
TextField(
value = text,
onValueChange = { text = it },
//placeholder = { Text("Placeholder") },
label = { Text("Label") },
visualTransformation = if (text.isEmpty())
PlaceholderTransformation("Placeholder")
else VisualTransformation.None,
textStyle = textStyle,
colors = TextFieldDefaults.textFieldColors(
textColor = textColor
)
)
with:
class PlaceholderTransformation(val placeholder: String) : VisualTransformation {
override fun filter(text: AnnotatedString): TransformedText {
return PlaceholderFilter(text, placeholder)
}
}
fun PlaceholderFilter(text: AnnotatedString, placeholder: String): TransformedText {
val numberOffsetTranslator = object : OffsetMapping {
override fun originalToTransformed(offset: Int): Int {
return 0
}
override fun transformedToOriginal(offset: Int): Int {
return 0
}
}
return TransformedText(AnnotatedString(placeholder), numberOffsetTranslator)
}
Upvotes: 7