z.g.y
z.g.y

Reputation: 6187

How to prevent initial onFocus trigger on TextField during first composition

Is there any way I can prevent onFocus trigger during the initial laying out of a composition?,

.onFocusChanged {
   // initial pass triggers un-necessary onFocus callback with just `false` values
   Log.e("TextFieldFocusChanged", "${it.isFocused} : ${it.hasFocus}")
 }

I have found this Google issue tracker that seems related to such behavior, though the issue is not exactly related to an initial pass of a composition.

This thing can be solved by specifying some boolean flag depending on your needs, but handling it this way slowly introduces complex boolean evaluations depending on your use-case. Is there any way I can configure a TextField to prevent onFocus callback on initial pass?

Thank you in advance.

Upvotes: 3

Views: 628

Answers (2)

Flaringapp
Flaringapp

Reputation: 235

Indeed you need to manage boolean flag for this. Firstly, create a flag state, and set it to true on first composition:

var isInitialCompositionCompleted by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
    isInitialCompositionCompleted = true
}

Secondly, use regular onFocusChanged modifier, and simply skip all callbacks until your flag is set to true:

Modifier.onFocusChanged { focusState ->
    if (!isInitialCompositionCompleted) return@onFocusChanged
    onFocusChanged(focusState.isFocused)
}

Here onFocusChanged(focusState.isFocused) is your outer lambda.

Upvotes: 3

z.g.y
z.g.y

Reputation: 6187

There must be a more efficient way to handle this but the most optimal approach I can do for now is to rely on SideEffect.

@Composable
fun ScreenTextFields() {

     //..TextField ... onFocusChanged { onTextFieldFocus(it) }...
     //..TextField ... onFocusChanged { onTextFieldFocus(it) }...
     //..TextField ... onFocusChanged { onTextFieldFocus(it) }...
     //..TextField ... onFocusChanged { onTextFieldFocus(it) }...

     SideEffect {
         // notify and update a boolean state (e.g onFieldsPostComposition)
     }
}

and in a state class.

fun onTextFieldFocus(focusState: FocusState) {
     if (onFieldsPostComposition) {
         // do intended use-case on actual focus changes
     }
}

This way, any initial onFocus callback can be ignored/skip for your intended logic, the only part I'm not sure if onFocusChanged { ... } callback is part of the composition/re-composition, not sure if there's a chance that SideEffect will occur first before onFocus callback, but so far, this approach works on my case..

Upvotes: 1

Related Questions