Thursday, 19 January 2023

How to persist keyboard across several composable screens?

Hello!

I have an issue with TextField in Jetpack Compose, Android.
We have a sequence of screens where each screen has TextField, and I want to keep the keyboard open when the screen is changed to the next or previous one. But now when I change the screen, the keyboard is closed and opens what looks terribly bad to the user.

Video: https://youtube.com/shorts/RmSPGT2Rteo


Example

In original, I have separate ViewModels connected to these screens, a lot of other components on them and navigation library to make the navigation concise. This is a very simplified sample of the issue I suffer from:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContent {
        var screenIndex by remember { mutableStateOf(0) }

        when (screenIndex) {
            0 -> Screen(0) { screenIndex = 1 }
            1 -> Screen(1) { screenIndex = 0 }
        }
    }
}

@Composable
fun Screen(
    index: Int,
    onButtonClick: () -> Unit,
) {
    Column(
        modifier = Modifier.fillMaxSize().imePadding(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
    ) {
        val focusRequester = remember { FocusRequester() }

        LaunchedEffect(Unit) {
            focusRequester.requestFocus()
        }

        var value by remember { mutableStateOf("$index") }
        TextField(
            modifier = Modifier.focusRequester(focusRequester),
            value = value,
            onValueChange = { value = it },
        )

        Button(onClick = onButtonClick) {
            Text("Change screen")
        }
    }
}

What I've tried to do

I've read the source code of the CoreTextField and learnt the following: There are special function which disposes the TextInputSession when TextField is removed from the composition.

Source from CoreTextField.kt, line 316. Compose Foundation version is 1.3.1

// Hide the keyboard if made disabled or read-only while focused (b/237308379).
if (enabled && !readOnly) {
    // TODO(b/230536793) This is a workaround since we don't get an explicit focus blur event
    //  when the text field is removed from the composition entirely.
    DisposableEffect(state) {
        onDispose {
            if (state.hasFocus) {
                onBlur(state)
            }
        }
    }
}

Also, I've tried the following things:

  1. Add the delay before opening the keyboard
  2. Disable TextField before changing the screen (partially works but in my project screen navigation happens in the ViewModel level and it's impossible to synchronize that processes)
  3. Use InputMethodManager to open the keyboard, especially it's toggleSoftInput method but it's deprecated.

How can I keep the keyboard opened and move focus to the new TextField when the screen is changed?



from How to persist keyboard across several composable screens?

No comments:

Post a Comment