1

I am trying to implement a TextField which inputs an amount and formats it as soon as it is typed and also limits it to 100,000.

@Composable
fun MainScreen(
    viewModel: MyViewModel
) {
    val uiState by viewModel.uiState.collectAsState()
    Column {
        AmountSection(
            uiState.amount,
            viewModel::updateAmount
        )
        Text(text = viewModel.logs)
    }
}

@Composable
fun AmountSection(
    amount: TextFieldValue,
    updateAmount: (TextFieldValue) -> Unit
) {
    BasicTextField(
        value = amount,
        onValueChange = updateAmount,
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number
    )
)

MyViewModel:

class MyViewModel: ViewModel() {

    private val _uiState = MutableStateFlow(MyUiState())
    val uiState: StateFlow<MyUiState> = _uiState

    var logs by mutableStateOf("")
    var text = ""

    fun updateAmount(amount: TextFieldValue) {
        val formattedAmount: String = amount.text.getFormattedAmount()
        text += "input = ${amount.text}\n"
        text += "output = $formattedAmount \n"
        logs = text
        _uiState.update {
            it.copy(amount = TextFieldValue(formattedAmount, TextRange(formattedAmount.length))
        }
    }    
}

data class MyUiState(val amount: TextFieldValue = TextFieldValue())

(logs and text are just for logging purpose. Was finding it difficult to share the logcat output so presented it this way)

Result:

enter image description here

  • When I press 6, the input is "12,3456" which is expected (ignore the currency)
  • My getFormattedAmount() function removes the last six as ( 123456 > 100000). It outputs "12,345" which is also correct. "12,345" is what gets displayed on the screen.
  • But when I press 7, I get the input "12,34567". Where did that 6 come from?? It was not in uiState.amount.

(Please ignore the last output line. getFormattedAmount only removes the last character if the amount exceeds the limit and it gave wrong output because it didn't expect that input)

I feel that I making some really silly mistake here and would be really thankful if somecome could help me find that out.

Arpit Shukla
  • 9,612
  • 1
  • 14
  • 40
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/238803/discussion-on-question-by-arpit-shukla-compose-textfield-shows-wierd-behavior). – Machavity Nov 02 '21 at 14:03

2 Answers2

0

Edit based on the edit:- From the looks of the question, it isn't much clear what you wish to achieve here, but this is my deduction - You just want a TextField that allows numbers to be input, but only up to a maximum value (VALUE, not characters). When a digit-press by a user leads to the value exceeding your max value, you want that digit to be not entered of course, but you wish to reflect no changes at all in this case, i.e., the field value should remain intact.

Based on the above deduction, here is an example:-

First of all, f your uiState variable. I'm keeping it simple for the sake of clarity.

class VM: ViewModel(){
 var fieldValue by mutableStateOf("")
 
 fun onFieldUpdate(newValue){
   if(newValue.toDouble() > 999999999999999)
   return
   else
   fieldValue = newValue
 }
}

@Composable
fun CrazyField(fieldValue: String, onFieldUpdate: (String) -> Unit){
 TextField(value = fieldValue, onValueChange = onFieldUpdate)
}

Do not comment further without actually running this.

Original answer:- Use a doubles parser.

var text by remember { mutableStateOf("") }
TextField(
    value = text,
    onValueChange = {
        if (it.toDouble() <= 100000)
            text = it //Based on your use-case - it won't cut off text or limit the amount of characters
       else text = it.subString(0,7) //In-case of pasting or fast typing
    },
    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
)
Richard Onslow Roper
  • 5,477
  • 2
  • 11
  • 42
  • If I get the explanation for this crazy behaviour, I will be able to implement what I need (hopefully). Otherwise, if i could just get the field, I will copy paste it in my code and move ahead. (Hoping not to encounter such issue in future ) – Arpit Shukla Nov 01 '21 at 08:56
  • Well, I think the native issue is because of the custom Data object you are using. Just try replacing it with the variable I already said. I have experienced issues with custom types which may be treated as mutable types by Compose, and so cannot be actively observed. When you use pre-defined immutable types like String and Int, they can be easily observed and you can be assured of proper recompositions occurring at right time stamps. – Richard Onslow Roper Nov 01 '21 at 11:42
0

Found this comment on Compose slack channel by Sean.

As a model, you should assume that the keyboard may make aribitrary and large edits each onValueChange. This may happen, for example, if the user uses autocorrect, replaces a word with an emoji, or other smart editing features. To correctly handle this, write any transformation logic with the assumption that the current text passed to onValueChange is unrelated to the previous or next values that will be passed to onValueChange.

So this is some issue with TextField & IME relationship.
I rewrote my getFormattedAmount function to format the given string without any assumptions (earlier it was assuming that amount is formatted till the last second character). Everything seems fixed now.

Arpit Shukla
  • 9,612
  • 1
  • 14
  • 40
  • I can share my new function if someone is interested. Didn't include right now since that wasn't the main focus of this question. – Arpit Shukla Nov 01 '21 at 12:24
  • Either it already was mentioned in the comments above, or wasn't because it might have seemed too obvious. You seem REALLY confused about what you wanted here. Are you going to mark this as the answer? This does not really explain the spooky behaviour it still is questionable. My recommendation would be to keep the question open. – Richard Onslow Roper Nov 01 '21 at 16:53
  • I was about to mark it as accepted. But if u say so, let's keep it open. – Arpit Shukla Nov 01 '21 at 17:44