Compose TextField Modifier.onFocusChanged {} event being called upon initialization even though no user focus interactions have been done
Asked Answered
A

2

11

Reproducible with code:

var value by remember { mutableStateOf("Hello\nWorld\nInvisible") }

TextField(
    value = value,
    onValueChange = { value = it },
    label = { Text("Enter text") },
    modifier = Modifier
        .onFocusChanged { Log.d("TAG", "1 OnFocusChangedCalled: $it") }
)

TextField(
    value = value,
    onValueChange = { value = it },
    label = { Text("Enter text") },
    modifier = Modifier
        .onFocusChanged { Log.d("TAG", "2 OnFocusChangedCalled: $it") }
)

Upon simply opening the view, Logcat shows:

D/TAG: 1 OnFocusChangedCalled: Inactive
D/TAG: 2 OnFocusChangedCalled: Inactive

With traditional XML layouts, onFocusChangedListener only triggers when user actually interacts with the field.

With compose layout, I'm trying to do error validation only when user removes focus from a TextField.

However this behaviour is causing my validation to trigger as soon as the view is initialized.

How can I make the onFocusChanged validation only occur after the user removes focus from the field, instead of upon initialization?

Ashil answered 30/9, 2022 at 1:29 Comment(0)
A
4

You can move the validation in the onValueChange attribute instead of the onFocusChanged modifier.

As workaround you can store a key when the a field gains the focus and then check this value when the focus is lost.

Something like:

 var focusedTextKey by remember { mutableStateOf("") }

 TextField(
    value = value,
    onValueChange = { value = it },
    label = { Text("Enter text") },
    modifier = Modifier
        .onFocusChanged {
            Log.d("TAG", "1 OnFocusChangedCalled: $it")

            if (it.isFocused) {
                focusedTextKey = "NAME"
            } else {
                if (focusedTextKey == "NAME") {
                    //validate the value
                }
                focusedTextKey = ""
            }
        }
    )
Albatross answered 30/9, 2022 at 13:55 Comment(1)
Thanks, I've decided to use this workaround for now, but using a boolean instead of a string key. Though it does seem like a bug that onFocusChanged is being called upon creation of the compose TextField.Ashil
C
0

You can simply make validation happen when you are done with the field and keyboard action onNext is called with the provided validation lambda block you want to execute.

OutlinedTextField(
  value = state.email,
  onValueChange = { event(SignupEvent.SetEmail(it)) },
  supportingText={ if(state.isErrorEmail) Text(text = state.errorEmail, maxLines = 1) },
  label={ Text("Email") },
  modifier = Modifier
    .fillMaxWidth(),
  keyboardActions = KeyboardActions(onNext = { event(SignupEvent.CheckEmailValidity) }),
  singleLine = true,
  isError = state.isErrorEmail,
  keyboardOptions = KeyboardOptions(
    // below line is used to specify our
    // type of keyboard such as text, number, phone.
    keyboardType = KeyboardType.Email,
    imeAction = ImeAction.Next
  )
)
Condign answered 29/2 at 10:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.