Skip to content

Instantly share code, notes, and snippets.

@sergejsha
Created August 4, 2023 20:02
Show Gist options
  • Save sergejsha/bbd4c6222289f8844cac488da759d088 to your computer and use it in GitHub Desktop.
Save sergejsha/bbd4c6222289f8844cac488da759d088 to your computer and use it in GitHub Desktop.
TextField911.kt for avoiding synchronization issues in standard BasicTextField
/**
* 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)
/**
* 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
}
}
}
}
/**
* 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,
)
}
/**
* 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