Is this a bug or am i using the new textfield API ...
# compose
a
Is this a bug or am i using the new textfield API wrong? I expect this outputTransformation to replace all text with
*
When I type stuff in, it works as expected, but when i press backspace once, the entire text is gone:
Copy code
val state = rememberTextFieldState(value)

BasicTextField(
    state = state,
    outputTransformation = {
        val text = state.text.toString().map { "*" }.joinToString("")
        replace(0, length, text)
    }
)
Something is up. On iOS it works as expected (backpresses removes the last *), while on Android and Desktop it removes the entire text
h
What you expect is that each character is replaced with
*
or entire text is replaced with a single
*
?
Is this a password field by any chance? Why not use BasicSecureTextField?
z
I believe that’s expected behavior. The output transformation sees your single replace call as a single block, so backspace operates on it as one unit. To get your behavior, you would want to replace each character individually with a separate replace call. But yeah, why not just use the thing that Halil suggested?
a
@Halil Ozercan I expect every character to be replaced by
*
. And when I press backspace only for the last character to be removed, not the entire text
@Zach Klippenstein (he/him) [MOD] the above is an example of me trying to understand the API In reality i am trying map the old
VisualTransformation
to use the new state based BasicTextField
To get your behavior, you would want to replace each character individually with a separate replace call.
I'll give it a go later. not sure how to do that, but i think i saw some sort of mapping property in there somwhere. thanks for the hint
btw, the reason why i expect all characters to be replaced by * is because i read this API as replace the current text from 0 to length with the text
text
that's what i understand from the documentation of replace as well
h
As Zach pointed out, OutputTransformation treats each replace call as an atomic operation. When you replace the entire text in a single call, OutputTransformation doesn't try to make sense of the relation between the changed string and the new string. It only looks at the replaced indices and assumes the newly inserted text entirely replaces the previous one, treats the new text as an indestructible single unit. If you are trying to create a one-to-one mapping between each character and its replacement, you need to call replace for each one. Also the current text is given to you in TextFieldBuffer, the receiver scope of OutputTransformation. Please do not read your TextFieldState inside the transformation, only rely on TextFieldBuffer.
s
This threw me off as well a while back, but yeah that's intended behavior as mentioned. You'll want to run
replace
for each single character independently. That's how Compose knows the mapping between input and output. Had to do the same for a Romaji to Kana converter for Japanese input.
a
@Halil Ozercan is all that documented somewhere?
h
We only have the following in the documentation which hints this behaviour but certainly it's not enough. Please create a bug for any doc improvement requests you might have
If there's a range of inserted characters via this OutputTransformation, selection or cursor never goes in between these inserted characters.
z
Input/OutputTransformation probably deserve a whole dedicated article.
2
i
Or
Copy code
TextField(
            password,
            onValueChange = { password = it },
            placeholder = { Text("Secured password field") },
            visualTransformation = PasswordVisualTransformation(),
            textStyle = MaterialTheme.typography.bodyMedium
        )