s

    Se7eN

    1 year ago
    [Code in thread 👇] I have this code where I'm using the
    pointerInput
    modifier to allow dragging inside my
    Box
    . The problem is when the
    locked
    parameter (and other parameters) change,
    onPositionChange
    and
    onDragStateChange
    still executes with the old value of the parameters. Like if
    locked
    was true by default and the state changes to false, the if statement in
    onPositionChange
    will still run. I don't know it might be related to some kotlin magic but I was using the now deprecated
    dragGestureFilter
    modifier before alpha12 and it was working fine with the same code. I've also tried using
    detectDragGestures
    inside
    pointerInput
    but no luck.
    fun Parent() {
        val locked by remember { mutableStateOf(true) }
        MyComposable(locked, ...)
    }
    
    fun MyComposable(locked: Boolean, ...) {
        val inputModifier = Modifier.simpleDragInput(
            onDragStateChange = { dragging -> ... },
            onPositionChange = { dragPosition ->
                if(locked) {
                    ...
                }
            }
        )
    
        Box(modifier = inputModifier)
    }
    
    fun Modifier.simpleDragInput(
        onPositionChange: (Offset) -> Unit,
        onDragStateChange: (Boolean) -> Unit,
    ): Modifier = pointerInput(Unit) {
        forEachGesture {
            awaitPointerEventScope {
                val down = awaitFirstDown()
                onDragStateChange(true)
                onPositionChange(down.position)
                drag(down.id) { change ->
                    change.consumePositionChange(
                        change.position.x - change.previousPosition.x,
                        change.position.y - change.previousPosition.y
                    )
                    onPositionChange(change.position)
                }
                onDragStateChange(false)
            }
        }
    }
    Vsevolod Ganin

    Vsevolod Ganin

    1 year ago
    This is because
    pointerInput(Unit)
    is remembering your first passed lambda for composition lifetime. Your should pass appropriate keys to make it update lambda
    Every instance of lambda captures local variables in turn. So your first pointerInput’s lambda remembers first
    onPositionChange
    and
    onDragStateChange
    and they remember first seen
    locked
    value
    s

    Se7eN

    1 year ago
    Ah I see. So I should pass all my parameters to pointerInput I'm using inside my lambdas? I have like 5-6 parameters.
    Vsevolod Ganin

    Vsevolod Ganin

    1 year ago
    I guess so, yes
    s

    Se7eN

    1 year ago
    Alright thanks
    Vsevolod Ganin

    Vsevolod Ganin

    1 year ago
    It’s the same story with
    DisposableEffect
    and
    LaunchedEffect
    . Moreover this behavior is due to the fact that
    pointerInput
    relies on
    LaunchedEffect
    in its implementation
    s

    Se7eN

    1 year ago
    Okay I added all my parameters but now the drag doesn't work properly. It only moves the drag position when I start the drag but after that the drag just stops working
    Vsevolod Ganin

    Vsevolod Ganin

    1 year ago
    I experienced similar problems, yes. It was because now my lambda was updating on every recomposition, cancelling previous efforts. Turned out, it was because of keys that I forgot to wrap with
    remember
    in the first place, so they were always new on every recomposition. So try to inspect each key if it is different on every recomposition
    manueldidonna

    manueldidonna

    1 year ago
    Try to pass your properties to rememberUpdatedState. It's an API to publish data from recomposition to ongoing or long-lived processes such as Disposable/LaunchedEffects
    s

    Se7eN

    1 year ago
    Still the same thing after passing keys with and without
    rememberUpdatedState
    . I do have a list key that is updated inside
    onPositionChanged
    using the drag position. I'll try making a minimal reproducible example
    Adam Powell

    Adam Powell

    1 year ago
    The parameters to
    pointerInput
    determine when your detector state machine will reset, and
    rememberUpdatedState
    lets you update things like these callbacks out from under the running
    pointerInput
    state machine without resetting it. The pattern @manueldidonna is describing above is:
    fun Modifier.simpleGesture(
      onGesture: () -> Unit
    ): Modifier {
      val currentOnGesture by rememberUpdatedState(onGesture)
    
      // Unit because we do not want to restart
      return pointerInput(Unit) {
        // ...
        // when detected, call currentOnGesture, not onGesture.
        // currentOnGesture is kept up to date by composition.
        currentOnGesture()
      }
    }
    s

    Se7eN

    1 year ago
    Got it, this works. Thanks everyone!
    One more thing: When should the state machine reset?
    Adam Powell

    Adam Powell

    1 year ago
    It depends on the use case. Possibly never, possibly when something deeply significant to the gesture changes such that old input in progress is invalid and things should be cancelled.
    A little hand-wavey, I know, sorry, I'm only on my first cup of coffee for the day 😅
    s

    Se7eN

    1 year ago
    Haha it's okay I get the general idea