Olivier Patry
04/12/2021, 2:44 PMpointerInput
Modifier
, I try to implement a custom drag behavior.
Based on a position on a Box
I compute a color.
If I use var color by remember { mutableStateOf(initialColor) }
inside my composable and update color value based on drag position it works.
If I extract a state (to allow parent composable to consume it), and hoist it, it doesn't work anymore.
My understanding is that the value captured by onDrag
is stale and still consider the initial value used and never update it. I don't understand the difference with state being hoisted or inline within Composable…
(code in thread)Olivier Patry
04/12/2021, 2:44 PMOlivier Patry
04/12/2021, 2:47 PMclass ColorPickerState(val originalColor: Color) {
var targetColor by mutableStateOf(originalColor)
}
@Composable
fun MainColorHueControl(
state: ColorPickerState,
enabled: Boolean = true,
) {
var origin by remember { mutableStateOf(Offset.Zero) }
val thumbSize = 20.dp
val wheelSize = 144.dp + thumbSize
val (hue, saturation, brightness) = state.targetColor.toHSB()
val hueAngle = Math.toRadians((360 - (hue * 360)).toDouble())
val halfSize = wheelSize / 2
val radius = (wheelSize - thumbSize) / 2
val position = Offset(
(radius.value * cos(hueAngle)).toFloat(),
(radius.value * sin(hueAngle)).toFloat()
)
val thumbX = (position.x).dp + halfSize - (thumbSize / 2)
val thumbY = (position.y).dp + halfSize - (thumbSize / 2)
Box(
Modifier.size(wheelSize)
.onSizeChanged { origin = it.center.toOffset() }
.pointerInput(Unit) {
detectDragGestures(
onDragStart = { offset ->
val next = offset - origin
state.targetColor = next.toColor(saturation, brightness)
},
onDrag = { change, amount ->
val next = position + amount
change.consumeAllChanges()
state.targetColor = next.toColor(saturation, brightness)
}
)
}
Olivier Patry
04/12/2021, 2:48 PMOlivier Patry
04/12/2021, 2:49 PMposition
in onDrag
we can see the value matches the position of the original color, not the last defined one)Olivier Patry
04/12/2021, 2:50 PMpointerInput(Unit)
, the drag sequence seems not working eitherAdam Powell
04/12/2021, 2:55 PMrememberUpdatedState
may be what you're looking forAdam Powell
04/12/2021, 2:58 PMAdam Powell
04/12/2021, 2:59 PMval currentSaturation by rememberUpdatedState(saturation)
and then use currentSaturation
in your gesture detection code instead of saturation
Olivier Patry
04/12/2021, 3:04 PMOlivier Patry
04/12/2021, 3:05 PMrememberUpdatedState
for position
(assimilated to hue
, it's a position on a circle), saturation
and brightness
.Olivier Patry
04/12/2021, 3:06 PMOlivier Patry
04/12/2021, 3:06 PMOlivier Patry
04/12/2021, 3:09 PMOlivier Patry
04/12/2021, 3:09 PMOlivier Patry
04/12/2021, 3:12 PMAdam Powell
04/12/2021, 3:16 PMAdam Powell
04/12/2021, 3:17 PMOlivier Patry
04/12/2021, 3:19 PMOlivier Patry
04/12/2021, 3:19 PMOlivier Patry
04/12/2021, 3:22 PMhue
.
Now, I can change the hue either by press or drag on the circle.
When I press, I change the state.targetColor
(based on clicked position on the circle transformed to hue) and my composable is recomposed, it works fine.
When I drag, I do the exact same thing but it doesn't work.
The difference is that the onDrag
relies on the position
being computed for previous step, because I get a "amount" of drag, not an absolute position so I need something to add.Olivier Patry
04/12/2021, 3:23 PMOlivier Patry
04/12/2021, 3:25 PMOlivier Patry
04/12/2021, 3:26 PMOlivier Patry
04/12/2021, 3:28 PMOlivier Patry
04/12/2021, 3:28 PMOlivier Patry
04/12/2021, 3:30 PMvar position by remember { mutableStateOf(Offset.Unspecified) }
val currentColor = when {
position != Offset.Unspecified -> Color.fromHSB(position.toHue(), saturation, brightness)
else -> originalColor
}
...
val (hue, _, _) = currentColor.toHSB()
...
.pointerInput(Unit) {
detectDragGestures(
...
onDrag = { change, amount ->
val prev = if (position == Offset.Unspecified) Offset.Zero else position
val next = prev + amount
position = next
change.consumeAllChanges()
}
Olivier Patry
04/12/2021, 3:31 PMAdam Powell
04/12/2021, 3:36 PMAdam Powell
04/12/2021, 3:36 PMdetectDragGestures
altogether and work with the events coming in using absolute coordinatesOlivier Patry
04/12/2021, 3:38 PMhmm, reading this again, yeah the data flow doesn't look right and this is deriving a new value from the same target that is being set
Olivier Patry
04/12/2021, 3:38 PMOlivier Patry
04/12/2021, 3:38 PMOlivier Patry
04/12/2021, 3:39 PMOlivier Patry
04/12/2021, 3:40 PMposition
, I add
var dragPosition by remember { mutableStateOf(Offset.Unspecified) }
and I consume this instead of position
in my detectDragGestures
implementationOlivier Patry
04/12/2021, 3:54 PMOlivier Patry
04/12/2021, 3:55 PM