I have a Composable in which a remembered value (an offset) needs to be updated both by the Composable itself and also from the calling side (using the Composable's arguments) -- how can I achieve this?
In particular, I have the following piece of code. The value I'm talking about is the offset
in NavigableBox
: I need to both be able to control it by dragging the box and by setting it manually using the value from OffsetInputField
which is passed as an argument.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Surface {
Box {
var boxOffset by remember { mutableStateOf(Offset.Zero) }
NavigableBox(boxOffset)
OffsetInputField { offset ->
offset.toFloat().let { boxOffset = Offset(it, it) }
}
}
}
}
}
}
@Composable
fun OffsetInputField(onInput: (String) -> Unit) {
var value by remember { mutableStateOf("") }
TextField(
value = value,
onValueChange = { value = it },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Go),
keyboardActions = KeyboardActions(onGo = { onInput(value) })
)
}
@Composable
fun NavigableBox(initOffset: Offset) {
var offset by remember(initOffset) { mutableStateOf(initOffset) }
Box(
modifier = Modifier
.fillMaxSize()
.pointerInput(Unit) { detectTransformGestures { _, pan, _, _ -> offset = pan } }
) {
Box(modifier = Modifier
.size(100.dp)
.offset { IntOffset(offset.x.roundToInt(), offset.y.roundToInt()) }
.background(Color.Blue)
)
}
}
In the current implementation the dragging works fine until a new value is passed as OffsetInputField
's input -- then the box stops responding to dragging. I assume it is because the MutableState
object containing the offset changes when gets recalculated and the box doesn't observe it anymore.
I already tried using unidirectional data flow in NavigableBox
(passing offset
value and onOffsetChange
lambda to it, but then dragging doesn't work as expected: the box just jiggles around its initial position, returning back to it when the gesture stops.
In case anyone interested, I'm developing an app where the draggable box is a map, and the text field is used for searching objects on it: the map is moved to be centered on an object entered.
CodePudding user response:
pointerInput
captures all the state variables used inside. With new initOffset
you're creating a new mutable state, but pointerInput
keeps updating the old reference.
You need to restart it by passing the same value(s) to key
:
pointerInput(initOffset) { /*...*/ }