How can I add a suffix to TextField
input that flows (moves) with the user input text?
Asked
Active
Viewed 974 times
4

Gabriele Mariotti
- 320,139
- 94
- 887
- 841

Mahozad
- 18,032
- 13
- 118
- 133
3 Answers
10
With M3 starting from the 1.1.0-alpha06
you can use the suffix
attribute:
TextField(
value = text,
onValueChange = { text = it },
suffix = { Text ("€") }
)
Before M3 1.1.0-alpha06
or with M2 or you can use the visualTransformation
attribute.
Something like:
TextField(
value = text,
onValueChange = { text = it },
singleLine = true,
visualTransformation = SuffixTransformation(" €"),
)
class SuffixTransformation(val suffix: String) : VisualTransformation {
override fun filter(text: AnnotatedString): TransformedText {
val result = text + AnnotatedString(suffix)
val textWithSuffixMapping = object : OffsetMapping {
override fun originalToTransformed(offset: Int): Int {
return offset
}
override fun transformedToOriginal(offset: Int): Int {
if (text.isEmpty()) return 0
if (offset >= text.length) return text.length return offset
}
}
return TransformedText(result, textWithSuffixMapping )
}
}
If you have the placeholder you can put a condition in the visualTransformation
attribute.
Something like:
TextField(
value = text,
onValueChange = { text = it },
singleLine = true,
visualTransformation = if (text.isEmpty())
VisualTransformation.None
else
SuffixTransformation(" €"),
placeholder = { Text("Placeholder") }
)

Gabriele Mariotti
- 320,139
- 94
- 887
- 841
-
Thanks for the answer. But this was a self-answered question. I think you didn't notice that :) What do you think of my answer? Actually, I derived it from [one of your posts](https://stackoverflow.com/a/67735317/8583692)! – Mahozad Nov 23 '22 at 12:17
-
Thank you for your answer, very useful (just check the suggestion of Martynas B, it's right.) If it can be useful to someone, I use this code to avoid overlapping when using BasicTextField with placeholder : ` var suffixLocal: String = "" // avoid to overlap placeholder if (!text.isEmpty()) suffixLocal = suffix val result:AnnotatedString = text + AnnotatedString(suffixLocal) val suffixOffset = suffixLocal.length ` – android_dev71 Jan 27 '23 at 16:59
-
1@android_dev71 thanks for the feedback. I've update the answer with the placeholder scenario. I would prefer to put a condition in the `visualTransformation` attribute just to have more flexibility. For example you can also handle the focus state with a placeholder. – Gabriele Mariotti Jan 27 '23 at 17:14
-
what is M3 and im not able to see any suffix property i can see only trailingIcon which accepts a compose object – Praneeth Aug 31 '23 at 13:35
-
1@Praneeth https://developer.android.com/jetpack/androidx/releases/compose-material3 – Gabriele Mariotti Aug 31 '23 at 13:37
-
@GabrieleMariotti how can I do this? when I try I'm able to see only trailingIcon and its adding some padding and looks weird for my suffix icon – Praneeth Aug 31 '23 at 13:40
2
I found Gabriele Mariotti's answer buggy. Needed to change transformedToOriginal
function to this:
override fun transformedToOriginal(offset: Int): Int {
if (offset > text.length) return text.length
return offset
}

Martynas B
- 2,843
- 2
- 12
- 15
-
1it's right, without this correction it gives errors like this, in my case happens with 2 fields, moving cursor by clicking from one to another o something else: `java.lang.IllegalStateException: OffsetMapping.transformedToOriginal returned invalid mapping: 5 -> 5 is not in range of original text [0, 4]` – android_dev71 Jan 27 '23 at 16:55
0
This is easily done in Compose like this:
const val SUFFIX = " $"
@Composable
fun SuffixedText() {
var text by remember { mutableStateOf("") }
TextField(
text,
singleLine = true,
visualTransformation = SuffixTransformer(SUFFIX),
onValueChange = { text = it }
)
}
class SuffixTransformer(val suffix: String) : VisualTransformation {
override fun filter(text: AnnotatedString): TransformedText {
val result = text + AnnotatedString(suffix)
return TransformedText(result, OffsetMapping.Identity)
}
}
The above component probably can be used in traditional Views too. See this post
Also, see the following:

Mahozad
- 18,032
- 13
- 118
- 133
-
It has some issues. For example if you click in the suffix (inside o just after the last character) the code crashed because of OffsetMapping.Identity – Gabriele Mariotti Nov 23 '22 at 14:51
-
I tied it in a Desktop app using [Compose Multiplatform](https://github.com/JetBrains/compose-jb) v1.2.1 which uses Compose v??? on Windows and did not encounter any error. Maybe it's a regression in newer versions of Compose? – Mahozad Nov 23 '22 at 15:20