Repository navigation
[Android] Non-default system Display size stops native ScrollView/Switch/TextInput from receiving touches (Fabric, Galaxy S23 Ultra) #58360
Description
Description
On a Samsung Galaxy S23 Ultra with the system Display size ("Screen zoom") set to anything other than the default, native Android views stop receiving touches from app launch under the New Architecture:
ScrollView/FlatListwill not scrollSwitchwill not flipTextInputwill not focus- native date-picker wheels will not spin
- the native bottom tab bar does not respond
Meanwhile gesture-handler-based touchables in the same tree work normally. Setting Display size back to default makes everything work again immediately. System font size is irrelevant — only Display size triggers it.
Up front about the limitations of this report (see "What I could not establish" below): I have not been able to produce a reproducer that fires outside this device, so this is a symptom report rather than a diagnosed bug. I am filing it because there appears to be no existing issue describing the touch symptom, and because the mechanism note below may be enough for someone with the affected hardware to confirm or refute quickly.
Environment
- react-native 0.86.2, New Architecture (Fabric), Hermes
- Expo SDK 57
- Samsung Galaxy S23 Ultra, One UI
- Trigger: Settings → Display → Display size, any non-default value
What I could not establish
- Not reproducible on a stock Android emulator.
adb shell wm density 480against a physical density of 420 is the same forced-density mechanism as Settings → Display size, but RN's plainScrollViewscrolled normally under it — including on a cold start with the override already applied (4 trials, all scrolled). So either the emulator path differs from One UI's implementation, or the trigger is narrower than "non-default density". - Device scope unknown. I only have confirmation on the S23 Ultra. I cannot say whether other Samsung models, other One UI versions, or non-Samsung devices are affected.
- No version bisect. I do not know whether this worked on an earlier RN release.
I am happy to gather anything specific on the affected device if a maintainer says what would be useful.
Possibly relevant: PixelUtil reads physical density, not configuration density
Offered as a lead, not a diagnosis.
DisplayMetricsHolder.initDisplayMetrics() stores two different things:
val displayMetrics = context.resources.displayMetrics // honours Display size
windowDisplayMetrics = displayMetrics
val screenDisplayMetrics = DisplayMetrics()
screenDisplayMetrics.setTo(displayMetrics)
wm.defaultDisplay.getRealMetrics(screenDisplayMetrics) // physical; ignores Display sizePixelUtil uses getScreenDisplayMetrics() — the getRealMetrics() one — for every dp↔px conversion (toPixelFromDIP, toPixelFromSP, toDIPFromPixel, getDisplayMetricDensity). A user's Display size setting changes resources.displayMetrics but not getRealMetrics(), so the two diverge exactly when this bug appears.
This also predicts something we observed: density overrides applied at both Activity and Application level had no effect, which is consistent with those overrides changing the configuration/window metrics that PixelUtil never reads.
I want to be clear that I have not verified this is the cause — it is a correlation between the code path and the trigger condition.
Related, though both describe layout/scaling rather than touch:
- 🖥️ [Android] PixelUtil uses getScreenDisplayMetrics() instead of getWindowDisplayMetrics() — breaks desktop/freeform/secondary display mode in RN 0.83+ #55659 —
PixelUtilusesgetScreenDisplayMetrics()instead ofgetWindowDisplayMetrics(), regressed in 0.83 (closed without a fix) - [Android] Text renders ~density× too large and clips on displays with a different density (Samsung DeX / freeform) — DisplayMetricsHolder is initialized once and never updated #57183 —
DisplayMetricsHolderinitialised once and never refreshed (open)
Workaround, for anyone who lands here
Routing touch through react-native-gesture-handler avoids it entirely — gesture-handler's ScrollView / FlatList on Android, and its pressables in place of RN's touchables. gesture-handler uses its own native-coordinate pipeline, which appears to be unaffected. This is what we shipped; it is a workaround, not a fix.
Activity
react-native-bot commentedon Sep 6, 2026
Warning
Missing reproducer: We could not detect a reproducible example in your issue report. Reproducers are mandatory and we can accept only one of those as a valid reproducer:
You can read more about about it on our website: How to report a bug.
khemraj-astro commentedon Sep 6, 2026
Reproducer (Snack)
The Snack opens straight from this link (source is inlined in the URL, hence the length):
https://snack.expo.dev/?platform=android&name=RN%20display-size%20touch%20repro&dependencies=react-native-gesture-handler%40%2A&code=//%20Repro%3A%20non-default%20Android%20%22Display%20size%22%20kills%20native%20touch%20handling.%0A//%0A//%201.%20Open%20on%20an%20affected%20Android%20device%20%28Galaxy%20S23%20Ultra%29%20in%20Expo%20Go.%0A//%202.%20Settings%20%3E%20Display%20%3E%20Display%20size%3A%20leave%20at%20DEFAULT%20-%3E%20everything%20works.%0A//%203.%20Set%20Display%20size%20to%20any%20NON-DEFAULT%20value%2C%20reopen%20-%3E%20the%20ScrollView%20will%0A//%20%20%20%20not%20scroll%2C%20the%20Switch%20will%20not%20flip%2C%20the%20TextInput%20will%20not%20focus.%0A//%20%20%20%20The%20gesture-handler%20ScrollView%20at%20the%20bottom%20keeps%20working.%0Aimport%20React%2C%20%7B%20useState%20%7D%20from%20%27react%27%3B%0Aimport%20%7B%20ScrollView%2C%20Switch%2C%20TextInput%2C%20Text%2C%20View%2C%20StyleSheet%20%7D%20from%20%27react-native%27%3B%0Aimport%20%7B%20ScrollView%20as%20GHScrollView%20%7D%20from%20%27react-native-gesture-handler%27%3B%0A%0Aexport%20default%20function%20App%28%29%20%7B%0A%20%20const%20%5Bon%2C%20setOn%5D%20%3D%20useState%28false%29%3B%0A%20%20const%20%5Btext%2C%20setText%5D%20%3D%20useState%28%27%27%29%3B%0A%20%20return%20%28%0A%20%20%20%20%3CView%20style%3D%7Bstyles.root%7D%3E%0A%20%20%20%20%20%20%3CText%20style%3D%7Bstyles.h%7D%3ERN%20ScrollView%20%28should%20scroll%29%3C/Text%3E%0A%20%20%20%20%20%20%3CScrollView%20style%3D%7Bstyles.box%7D%3E%0A%20%20%20%20%20%20%20%20%7BArray.from%28%7B%20length%3A%2030%20%7D%2C%20%28_%2C%20i%29%20%3D%3E%20%28%0A%20%20%20%20%20%20%20%20%20%20%3CText%20key%3D%7Bi%7D%20style%3D%7Bstyles.row%7D%3ERN%20row%20%7Bi%7D%3C/Text%3E%0A%20%20%20%20%20%20%20%20%29%29%7D%0A%20%20%20%20%20%20%3C/ScrollView%3E%0A%0A%20%20%20%20%20%20%3CText%20style%3D%7Bstyles.h%7D%3ERN%20Switch%20/%20TextInput%3C/Text%3E%0A%20%20%20%20%20%20%3CView%20style%3D%7Bstyles.inline%7D%3E%0A%20%20%20%20%20%20%20%20%3CSwitch%20value%3D%7Bon%7D%20onValueChange%3D%7BsetOn%7D%20/%3E%0A%20%20%20%20%20%20%20%20%3CTextInput%0A%20%20%20%20%20%20%20%20%20%20style%3D%7Bstyles.input%7D%0A%20%20%20%20%20%20%20%20%20%20value%3D%7Btext%7D%0A%20%20%20%20%20%20%20%20%20%20onChangeText%3D%7BsetText%7D%0A%20%20%20%20%20%20%20%20%20%20placeholder%3D%22tap%20to%20focus%22%0A%20%20%20%20%20%20%20%20/%3E%0A%20%20%20%20%20%20%3C/View%3E%0A%0A%20%20%20%20%20%20%3CText%20style%3D%7Bstyles.h%7D%3Egesture-handler%20ScrollView%20%28control%20%E2%80%94%20keeps%20working%29%3C/Text%3E%0A%20%20%20%20%20%20%3CGHScrollView%20style%3D%7Bstyles.box%7D%3E%0A%20%20%20%20%20%20%20%20%7BArray.from%28%7B%20length%3A%2030%20%7D%2C%20%28_%2C%20i%29%20%3D%3E%20%28%0A%20%20%20%20%20%20%20%20%20%20%3CText%20key%3D%7Bi%7D%20style%3D%7Bstyles.row%7D%3EGH%20row%20%7Bi%7D%3C/Text%3E%0A%20%20%20%20%20%20%20%20%29%29%7D%0A%20%20%20%20%20%20%3C/GHScrollView%3E%0A%20%20%20%20%3C/View%3E%0A%20%20%29%3B%0A%7D%0A%0Aconst%20styles%20%3D%20StyleSheet.create%28%7B%0A%20%20root%3A%20%7B%20flex%3A%201%2C%20padding%3A%2012%2C%20paddingTop%3A%2048%20%7D%2C%0A%20%20h%3A%20%7B%20fontWeight%3A%20%27600%27%2C%20marginTop%3A%2012%2C%20marginBottom%3A%204%20%7D%2C%0A%20%20box%3A%20%7B%20flex%3A%201%2C%20borderWidth%3A%201%2C%20borderColor%3A%20%27%23999%27%20%7D%2C%0A%20%20row%3A%20%7B%20padding%3A%2012%20%7D%2C%0A%20%20inline%3A%20%7B%20flexDirection%3A%20%27row%27%2C%20alignItems%3A%20%27center%27%2C%20gap%3A%2012%20%7D%2C%0A%20%20input%3A%20%7B%20flex%3A%201%2C%20borderWidth%3A%201%2C%20borderColor%3A%20%27%23999%27%2C%20padding%3A%208%20%7D%2C%0A%7D%29%3B%0A
Inlining the source too, since the trigger is a system setting rather than anything unusual in the code:
Steps
The gesture-handler ScrollView is included deliberately as an in-app control: on an affected device the contrast between the two lists is visible immediately, which should make a yes/no call quick for anyone with the hardware.
Status of this reproducer - please read
I want to be straight about what is and isn't verified, rather than have this bounce a second time:
adb shell wm density 480against a physical 420 did not reproduce it (4 trials, all scrolled).I'll follow up here with the result as soon as it's run on the device: either a confirmation with the One UI build number, or - if this Snack turns out not to fail there - I'll say so plainly, since that would mean the cause is in our stack rather than in React Native and this issue should be closed.
khemraj-astro commentedon Sep 7, 2026
Update after on-device testing — one correction to this report, and the lead I offered is refuted
1. Correction:
ScrollViewis the wrong headlineThe title and opening of this issue lead with
ScrollView, and that turns out to be the one primitive that does work. On-device testing at a non-default Display size:ScrollView— scrolls normally at every Display size triedSwitch(won't flip),TextInput(won't focus), the native date-picker wheels (won't spin),FlatList,SectionList, andRefreshControl/ pull-to-refresh (onRefreshnever fires)gesture-handler's equivalents keep working in the same tree, as originally described. Apologies for the misleading framing — I'd have edited the title but I'd rather leave the record visible than quietly rewrite it.
2. Second affected device — not S23-Ultra-only
Reproduced on a Samsung SM-G990E (Galaxy S21 FE), Android 16 / One UI:
dumpsys displaycarries both values for the same panel —mBaseDisplayInfo ... density 480andmOverrideDisplayInfo ... density 420.Setting the density back to the panel default made the failing behaviour go away and restoring the override brought it back, so the Display size override is definitely the trigger.
3. Refuted: the
PixelUtil/ physical-density lead from the original reportI suggested above that
PixelUtilreadinggetScreenDisplayMetrics()(i.e.Display.getRealMetrics()) might diverge from the configuration density. That is not what happens. Measured in-app across four override values:Dimensions.get('window').scaleDimensions.get('screen').scalegetRealMetrics()returns the override, not the base, so React Native sees one consistent density even while the bug is active. Please disregard that part of the original report — and by extension, "makePixelUtilusegetWindowDisplayMetrics()" and "callDisplayMetricsHolder.setScreenDisplayMetrics(resources.displayMetrics)" would both be no-ops here. There is nothing to reconcile.So whatever mishandles the override is somewhere other than the density value RN exposes to JS.
4. On the reproducer
Still not device-confirmed, and I want to be straight about why rather than let it look stronger than it is. My injected-gesture harness turned out to be confounded: the drag started at a fixed pixel offset while the layout rescales with density, so at one density it landed inside a horizontal carousel — and a horizontal scroller swallowing the gesture is indistinguishable from the bug. I briefly inferred a density-value pattern from that and am retracting it.
A trustworthy harness needs the gesture target expressed in dp and converted per density, a scroll-to-top first, and repeated trials per density. If a maintainer confirms that's worth doing, I'll build it and post the result; equally, if this stays unreproducible outside Samsung hardware I have no objection to it being closed.
khemraj-astro commentedon Sep 8, 2026
Resolved — root cause found, and it is not React Native. Closing.
The touch loss at a non-default Display size was caused by
@gorhom/bottom-sheet: a permanently mounted, closedBottomSheet'sBottomSheetBackdropstayspointerEvents: 'auto'as an invisible full-screen view whenever the device's screen height in dp is not an integer. Filed with the full analysis and a Snack at gorhom/react-native-bottom-sheet#2764.What we measured on the S23 Ultra at Display size → density 2.625 (screen 2340 px → 891.4285… dp):
animatedIndexis-0.99999998, not-1: the sheet's initial position is a JS-doubleDimensionsconstant (891.4285714285714) while the container height its index math uses is the float32 layout value (891.4285888671875), and a never-opened sheet is never re-synced. The backdrop gates touchability with an exactanimatedIndex <= -1, so it never turns itspointerEventsoff.BottomSheetBackdropComponent < BottomSheet < … < UpdateAvailableSheet— a closed sheet mounted at the app root.dumpsys activity topshows it as a full-screen, childlessReactViewGroupdrawn above the whole navigator.TouchTargetHelperresolves the backdrop as the JS target (soPressabledies), and aReactViewGroupwithpointerEvents: autoreturnstruefromonTouchEvent, so it also consumes the native touch beforeSwitch/EditText/ScrollView/the picker wheels beneath it (so native controls die). gesture-handler's own hit-testing skips views with alpha < 0.1, which is why every GH control kept working — the observation that sent me looking at RN's density code.So the density values RN exposes were consistent all along (as the previous comment showed), and the Display-size setting was only the trigger for a fractional dp height. Apologies for the noise — leaving the record here in case anyone lands on the same symptom via search: check for a mounted, closed bottom sheet with a backdrop first.