android-jetpack-composeandroid-jetpack-datastore

Cursor is jumping one position to the left while typing when i try to save the value of an OutlinedTextField within onValueChange


I'm having some problems using the OutlinedTextField and connecting to my DataStore where I store my user settings. The data is saved and read correctly. However, there seems to be a problem updating the display and the cursor. When I enter a text (probably because I was typing too fast), the cursor jumps one position to the left. This corrupts the input and the user experience is pretty bad.

So far I haven't been able to find a solution to fix the problem. Do you have an idea what it could be? Thank you very much!

Thats the code i use for my OutlinedTextField:

OutlinedTextField(
    value = dataStoreState.nickname,
    onValueChange = {
        CoroutineScope(Dispatchers.IO).launch {
            viewModel.saveToDataStore(dataStoreState.copy(nickname = it))
        }
    },
    label = { Text(stringResource(id = R.string.enterNickname)) },
    keyboardOptions = KeyboardOptions.Default.copy(
        keyboardType = KeyboardType.Text,
        imeAction = ImeAction.Done
    ),
    modifier = Modifier.fillMaxWidth()
) 

I tried to cache the text value in another variable, that had no effect. Then I tried to change the save function, but I couldn't find a correct solution here either.


Solution

  • Updates to the text field state must be synchronous, you can't have a coroutine updating your store and only update the text field after that completes. You will have to rework this to ensure the state updates immediately, then you can maybe push the current text to a snapshotflow, debounce that and update from the flow.

    Something like this

    var text by remember {
        mutableStateOf("")
    }
    LaunchedEffect(key1 = Unit) {
        text = datastoreState.nickname
        snapshotFlow { text }
            .debounce(500L)
            .distinctUntilChanged()
            .onEach {
                viewModel.saveToDataStore(dataStoreState.copy(nickname = it))
            }
            .launchIn(this)
    }
    OutlinedTextField(
        value = text,
        onValueChange = { text = it },
        label = { Text(stringResource(id = R.string.enterNickname)) },
        keyboardOptions = KeyboardOptions.Default.copy(
            keyboardType = KeyboardType.Text,
            imeAction = ImeAction.Done
        ),
        modifier = Modifier.fillMaxWidth()
    )