Skip to content

Screen behind the sheet flashes through during a slow pan-down-to-close on Android (New Architecture) #2716

Description

@samiullaansari

[Bug]: Screen behind the sheet flashes through during a slow pan-down-to-close on Android (New Architecture)

Version

5.2.14

Reanimated Version

v4 (4.5.0 — not in the dropdown options above, which only list v3/v2/v1; noting the real version here since it may be relevant)

Gesture Handler Version

2.32.0

Platforms

Android

What happened?

During enablePanDownToClose, if you drag the sheet down slowly (not a quick flick), somewhere in the middle of the drag the screen content behind the sheet becomes visible for a moment — you can read actual text/rows from the screen underneath — before the sheet finishes animating off-screen and the modal fully dismisses. It only reproduces on a slow, held drag; a fast flick-to-close doesn't show it (or it's too brief to notice).

This reproduces with a bare-minimum BottomSheetModal — no custom handleComponent, no custom backdropComponent styling, no enableDynamicSizing, just snapPoints + the default BottomSheetBackdrop. So it isn't specific to any particular app-level configuration.

What we ruled out

We instrumented onChange/onAnimate and Android's Dimensions change event around the close gesture. No JS-side event (no resize, no index-change callback) fires during the flash — it happens purely on the UI thread mid-gesture, before the release-triggered onAnimate spring even starts. That means it isn't triggered by a container/window resize event reaching JS.

We also tried patching BottomSheetBody.tsx's Android-only opacity behavior:

- opacity: Platform.OS === 'android' && animatedIndex.get() === -1 ? 0 : 1,
+ opacity: Platform.OS === 'android' && animatedSheetState.get() === SHEET_STATE.CLOSED ? 0 : 1,

(tying the opacity drop to the same position >= closedDetentPosition check that drives animatedSheetState, instead of the separately-derived animatedIndex, so opacity would only hit 0 once animatedPosition — the same value driving translateY — has actually reached the closed offset). This did not fix the flash even after a full rebuild (expo prebuild + expo run:android --no-build-cache), which rules out that specific opacity/position desync as the (sole) cause.

Given it survives both of those, and the flash shows real screen content (not a blank/white frame or a stale sheet frame), our best guess is a native Fabric view-compositing/paint-order race for the portal-rendered sheet content during a slow, continuous UI-thread-driven transform — not something reachable from JS/Reanimated style values. Filing this in case others are hitting the same thing under New Architecture, and in case there's a mounting/z-order angle in the native side that isn't visible from the JS layer.

Screen recording

Attached: gorhom-flash-repro.mp4 (compressed from the original device capture, no audio). It shows several open/close cycles; the flash is brief, so scrubbing frame-by-frame or slowing playback down around any of the drag-to-close moments is the easiest way to catch it — it reads as actual list content from the screen behind briefly showing through the sheet.

Reproduction steps

  1. Enable New Architecture (newArchEnabled=true).
  2. Render a plain BottomSheetModal with snapPoints, enablePanDownToClose, and the default BottomSheetBackdrop — content is a scrollable list with distinct, readable rows. The screen behind the sheet is also a distinct, readable list (so a flash is unambiguous).
  3. Present the sheet.
  4. Use the pan-down-to-close gesture and drag it closed slowly, holding a controlled, steady downward motion rather than a flick.
  5. Watch the area behind the sheet partway through the drag.

Reproduction sample

https://snack.expo.dev/@laabhplus/bottom-sheet---issue-reproduction-template

Important caveat: the Snack template pins @gorhom/bottom-sheet@5.1.5 and react-native-reanimated@~3.17.4 (Reanimated 3) — our actual repro is on 5.2.14 + Reanimated 4.5.0 under React Native's New Architecture, which we believe is central to this bug (see Environment below). Expo Snack's preview likely doesn't run under the New Architecture, so this Snack may not visibly reproduce the flash as-is — it demonstrates the minimal component setup (plain BottomSheetModal, default backdrop, no custom styling), but the actual bug needs a New-Architecture Android build to see. Our device recording (below) shows it happening on that real setup.

Environment

@gorhom/bottom-sheet 5.2.14
react-native 0.86.0
react-native-reanimated 4.5.0
react-native-gesture-handler 2.32.0
react-native-worklets 0.10.0
react 19.2.3
expo 57
New Architecture enabled (newArchEnabled=true)
Platform Android only (haven't been able to compare against iOS)
Device Xiaomi Redmi Note 11 (model 2201117TI), Android 13 (SDK 33), MIUI V816.0.6.0

Relevant log output

No error/warning output — purely visual. onChange/onAnimate log during a close (for reference, index/position pairing looks correct on the JS side; the flash happens before onAnimate even fires, mid-gesture):

onAnimate {"fromIndex": 0, "fromPosition": 794.06, "toIndex": -1, "toPosition": 830.91}
onChange {"index": -1}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions