Skip to content

fix: drive horizontal scrolling with a pan gesture (Android flicker) - #2

Merged
ahmdshrif merged 2 commits into
mainfrom
fix/android-pinned-flicker
Sep 19, 2026
Merged

ahmdshrif merged 2 commits into
mainfrom
fix/android-pinned-flicker

Conversation

@ahmdshrif

@ahmdshrif ahmdshrif commented Sep 19, 2026 •

Copy link
Copy Markdown
Owner

Fixes #3

Cause

The body scrolled in a native ScrollView, while the header and pinned columns were positioned from a Reanimated value fed by that ScrollView's scroll events. On Android the two do not land in the same frame, so the pinned columns drift from the rows and snap back on the next frame. iOS keeps them in sync, which matches the report being Android-only.

A frame from the reporter's recording shows the pinned columns shifted right, with the Amount column visible to their left, while the frames on either side are correct.

Fix

A pan gesture drives one shared value. The rows, the header and the pinned columns all read it in the same frame, so they cannot disagree.

  • Adds react-native-gesture-handler as a peer dependency; apps wrap their root in GestureHandlerRootView (README updated).
  • Trade-off: no native horizontal scrollbar or overscroll bounce on the sideways axis. Vertical scrolling is unchanged (still FlashList).

An earlier commit on this branch removed zIndex from the pinned group, which was a real Android drawing-order hazard but not the cause of this bug. It is kept because the absolute-overlay layout is what the pan approach builds on.

Testing

A detector flags frames that jump away from both neighbours while those neighbours match each other, which is what a flicker looks like:

Recording Frames Flicker frames
Reporter's video, old build 1,135 70
Same swipe sequence, old build 310 7
Same swipe sequence, this branch 358 0
  • Android emulator (API 35), Release: flicker gone, confirmed by the reporter.
  • iOS 26 simulator, Release: sideways scrolling, pinned columns and vertical scrolling all correct.
  • 37 unit tests, lint and typecheck pass.
  • Not yet verified on a physical device.

Android implements zIndex by reordering views at draw time, which can flicker
while a transform animates. The pinned group is now the last child of the row
and header, positioned absolutely over a spacer, so it draws on top without
reordering.
The body scrolled in a native ScrollView while the header and pinned columns
were positioned from a Reanimated value fed by scroll events. On Android the
two do not land in the same frame, so the pinned columns drifted and snapped
back, which read as flicker.

A pan gesture now drives one shared value that positions the rows, the header
and the pinned columns in the same frame. Adds react-native-gesture-handler as
a peer dependency; apps must wrap their root in GestureHandlerRootView.

Measured on the Android emulator with a frame detector that flags frames
jumping away from both neighbours while the neighbours match: 7 such frames
before the change, 0 after, over the same swipe sequence.
@ahmdshrif ahmdshrif changed the title fix: draw pinned columns without zIndex to stop Android flicker fix: drive horizontal scrolling with a pan gesture (Android flicker) Sep 19, 2026
@ahmdshrif
ahmdshrif merged commit 0794df3 into main Sep 19, 2026
6 checks passed
@ahmdshrif
ahmdshrif deleted the fix/android-pinned-flicker branch September 19, 2026 16:26
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.

Pinned columns flicker on Android when scrolling sideways

1 participant