JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
15.3k stars 1.11k forks source link

[iOS] voice dictation text does not register with text fields using TextFieldValue #5044

Open garrison-henkle opened 4 days ago

garrison-henkle commented 4 days ago

Describe the bug When using the iOS stock keyboard's voice dictation in a BasicTextField using TextFieldValue, the dictated text is cleared when voice dictation is disabled.

Affected platforms

Versions

To Reproduce Steps to reproduce the behavior:

  1. Create a BasicTextField using TextFieldValue e.g.
    var textState by remember { mutableStateOf(TextFieldValue()) }
    BasicTextField(
    value = textState,
    onValueChange = { textState = it },
    )
  2. Click on the voice dictation button on the stock iOS keyboard
  3. Dictate
  4. Tap on the screen to end the dictation
  5. The text will disappear as soon as dictation ends

Repo that reproduces the issue on my device: https://github.com/garrison-henkle/composeMultiplatformVoiceDictationBug

Expected behavior The text does not disappear when dictation ends

Screenshots https://github.com/JetBrains/compose-multiplatform/assets/70973354/d4997920-0a77-479e-a5a9-26b7be7259a1

Additional context

garrison-henkle commented 3 days ago

I played around with the debugger / print statements for a bit this afternoon. To reproduce in the compose-multiplatform-core repo, I added the following to androidx.compose.mpp.demo.textfield.android.TextFieldValueDemo:

item {
    TagLine("TextFieldValue overload")
    var state by remember { mutableStateOf(TextFieldValue()) }
    BasicTextField(
        value = state,
        onValueChange = { state = it },
        textStyle = TextStyle(fontSize = fontSize8),
        modifier = demoTextFieldModifiers
    )
}

So far, the issue seems be somewhere afteronValueChangeOriginal is called in CoreTextField's onValueChange here. It is called with the correct TextFieldValue containing the dictated text, but the callback in the BasicTextField that I'm using receives what looks like a brand new empty TextFieldValue instance instead.

There is a currentRecomposeScope.invalidate() that is called immediately after the onValueChangeOriginal call, so I wonder if that is breaking something.