Skip to content

Stop a gesture area's swipe also pressing a pressable on Android - #182

Merged
simonhamp merged 2 commits into
mainfrom
fix/android-swipe-consumes-tap
Oct 28, 2026
Merged

simonhamp merged 2 commits into
mainfrom
fix/android-swipe-consumes-tap

Conversation

@simonhamp

Copy link
Copy Markdown
Member

On Android, swiping across a pressable inside a native:gesture-area fires both the area's @swipe and the pressable's @press. The same goes for a pressable around the area. iOS only fires the swipe, because a tap there fails once the touch moves. Found in NativePHP/dev-null-app#102, where swiping a long message bubble opened its actions and the same gesture's tap closed them again.

Cause

The swipe's pointerInput in GestureAreaRenderer.kt adds up the pan without consuming anything. Compose's clickable only cancels a tap when the pointer leaves its bounds or a change is consumed. A horizontal swipe that ends inside the bubble is neither, so the tap goes through on release.

Fix

Once the pan passes touch slop with the swipe's finger count, the swipe looks at the Final pass of that event. If nothing has consumed it yet, the swipe takes the drag and consumes every change for the rest of the gesture.

  • A pressable inside the area checks the Final pass for a consumed change, so it cancels on that event.
  • A pressable around the area sees the later changes and the release already consumed in the Main pass, so it cancels too.
  • An enclosing scroll container, or the area's own pan drag, consumes the event where it passes slop. The swipe sees that in the Final pass and leaves the drag to them. A vertical drag through a list of swipeable rows still scrolls the list.

@swipe still fires when something else owns the drag, as before, since the swipe adds up the pan whether or not a change was consumed.

Worth a look

  • Once a horizontal swipe has taken the drag, an enclosing vertical scroll container won't start scrolling for the rest of that gesture. That's how Android usually treats a swipe on a row. On iOS the swipe recogniser runs alongside the scroll view's pan, so the list can drift a little during a swipe there.
  • An area that binds pan-x and gets a vertical swipe (or pan-y and a horizontal one) now keeps that gesture for the swipe, so the pan doesn't move during it. A swipe along the bound axis is unchanged, since the pan drag takes it first.
  • A two-finger swipe over a one-finger area still taps the pressable underneath, before and after. That swipe isn't the area's, so it doesn't consume it.

Tests

tests/android gets GestureAreaSwipeTest, six Robolectric cases: one-finger and two-finger swipes over a pressable inside the area, a swipe inside a pressable around the area, a plain tap, a vertical drag in a verticalScroll, and an area with a bound pan-x. The three swipe-over-pressable cases fail on main with press events next to the swipe, and all six pass here. The existing 12 focused-input cases still pass.

/path/to/mobile/resources/androidstudio/gradlew -p tests/android -PmobileRoot=/absolute/path/to/mobile testDebugUnitTest

The module now also stages GestureAreaRenderer.kt and core's SharedValueStore.kt, and has a small NodeView stand-in that draws each child as a box, clickable like a core pressable.

On a device

Android emulator (API 35), a test screen in the dev-null app with a gesture area around a long pressable bubble, a pressable around a gesture area and a two-finger area, all in a scroll-view. Two-finger swipes were injected as real two-pointer touch events.

Step main This branch
Swipe left, then right, on the bubble swipe and tap each time swipe only
Swipe inside the pressable around the area swipe and tap each time swipe only
Two-finger swipe on the two-finger area swipe and tap each time swipe only
Vertical drag starting on the bubble list scrolls, no tap list scrolls, no tap
Plain tap on each pressable tap tap

iOS isn't touched.

Once this is tagged, dev-null-app can drop Conversation::SwipeTapWindow, its 0.75 s workaround.

🤖 Generated with Claude Code

simonhamp and others added 2 commits October 28, 2026 14:12
… tap

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…droid

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@simonhamp
simonhamp marked this pull request as ready for review October 28, 2026 17:01
@simonhamp
simonhamp merged commit f468aa2 into main Oct 28, 2026
4 checks passed
@simonhamp
simonhamp deleted the fix/android-swipe-consumes-tap branch October 28, 2026 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant