Created
August 4, 2023 20:02
-
-
Save sergejsha/bbd4c6222289f8844cac488da759d088 to your computer and use it in GitHub Desktop.
TextField911.kt for avoiding synchronization issues in standard BasicTextField
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright 2023 Sergej Shafarenka, www.halfbit.de | |
* Licensed under the Apache License, Version 2.0 | |
*/ | |
package de.halfbit.textfield911 | |
data class EditorString( | |
val value: String, | |
val modelVersion: Int, | |
val editorVersion: Int, | |
) { | |
// This method is used by TextField911 | |
fun copyInEditor(value: String): EditorString = | |
EditorString(value, modelVersion, editorVersion + 1) | |
// Use this method for modifying the value in your model, when modification doesn NOT originate from TextField911 | |
fun copyInModel(value: String): EditorString = | |
EditorString(value, modelVersion + 1, 0) | |
} | |
fun editorString(value: String): EditorString = | |
EditorString(value, 0, 0) | |
fun emptyEditorString(): EditorString = emptyEditorString | |
private val emptyEditorString: EditorString = | |
EditorString("", 0, 0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright 2023 Sergej Shafarenka, www.halfbit.de | |
* Licensed under the Apache License, Version 2.0 | |
*/ | |
import kotlinx.coroutines.flow.StateFlow | |
interface WelcomeModel { | |
data class State( | |
val userName: EditorString = emptyEditorString(), | |
) | |
val state: StateFlow<State> | |
fun onUserNameEdited(userName: EditorString) | |
} | |
internal class DefaultWelcomeModel() : WelcomeModel { | |
override fun onUserNameEdited(userName: EditorString) { | |
// Here we get a modification of the value from TextField911 | |
// The example `stateHolder.updateState` call should be replaced by your reducer code. | |
// | |
// You can update state asynchronously in any dispatcher. My code runs in Default dispatcher. | |
// | |
// Just make sure that the state update is executed fast enough to handle user's input. Otherwise | |
// the state in the TextField911 and in the model's state will stay different until all state | |
// updates are processed and the states are eventually converged. | |
stateHolder.updateState { state -> | |
if (userName.modelVersion == state.userName.modelVersion) { | |
// The modification is based on the version in State -> accept it as is, without any changes | |
state.copy(userName = userName) | |
} else { | |
// The modification is not based on the version in State, so it's outdated -> ignore it | |
state | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright 2023 Sergej Shafarenka, www.halfbit.de | |
* Licensed under the Apache License, Version 2.0 | |
*/ | |
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.collectAsState | |
import androidx.compose.runtime.getValue | |
import de.halfbit.textfield911.TextField911 | |
@Compose | |
fun SampleView( | |
model: SampleModel, | |
) { | |
val state by model.state.collectAsState() | |
TextField911( | |
value = state.userName, | |
onValueEdited = model::onUserNameEdited, | |
) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright 2023 Sergej Shafarenka, www.halfbit.de | |
* Licensed under the Apache License, Version 2.0 | |
*/ | |
package de.halfbit.textfield911 | |
import androidx.compose.foundation.text.BasicTextField | |
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.getValue | |
import androidx.compose.runtime.mutableStateOf | |
import androidx.compose.runtime.remember | |
import androidx.compose.runtime.setValue | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.text.TextStyle | |
import de.halfbit.textfield911.model.EditorString | |
@Composable | |
fun TextField911( | |
value: EditorString, | |
onValueEdited: (EditorString) -> Unit, | |
modifier: Modifier = Modifier, | |
textStyle: TextStyle = TextStyle.Default, | |
) { | |
var currentValue by remember { mutableStateOf(value) } | |
if (value.modelVersion > currentValue.modelVersion) { | |
// The value is NOT a confirmation of a change previously sent by this TextField911 | |
// to the model, but an actual change done in the model using EditorString.copyInModel() method. | |
currentValue = value | |
} // else this is a value from this TextField911 -> ignore it | |
BasicTextField( | |
value = currentValue.value, | |
onValueChange = { changedValue -> | |
currentValue = currentValue.copyInEditor(changedValue) | |
onValueEdited(currentValue) | |
}, | |
modifier = modifier, | |
textStyle = textStyle, | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment