-
-
Notifications
You must be signed in to change notification settings - Fork 615
Description
Summary
On Fabric (new architecture), when a ScrollView/FlatList is nested inside intermediate Views within a formSheet screen (e.g., SafeAreaProvider → View → wrapper View → FlatList), the ScrollView overlaps sibling Views (header) and bottom content is clipped by the header's height.
The root cause: childRCTScrollViewComponentAndContentContainer in RNSScreenContentWrapper.mm only searches direct subviews of the content wrapper. Any intermediate Views (SafeAreaProvider, layout boundary Views) hide the ScrollView from the algorithm, so coerceChildScrollViewComponentSizeToSize returns NO and the Fabric (0,0) placement bug goes unfixed.
The Fabric (0,0) bug
The maintainers already identified this in the source (RNSScreenContentWrapper.mm L174-L182):
#ifdef RCT_NEW_ARCH_ENABLED
// For some unknown yet reason on new architecture the scroll view
// is placed at (0, 0), which makes no sense given there
// is another child at lower index in flex layout.
// TODO: Research why this happens and solve this properly.
if (newFrame.origin.y == 0) {
newFrame.origin.y = headerView.frame.size.height;
}
#endifThis workaround correctly fixes Case 2 (header at index 0, ScrollView at index 1), but only when the ScrollView is a direct subview of RNSScreenContentWrapper.
Reproduction
Layout hierarchy that triggers the bug:
RNSScreenContentWrapper
└── SafeAreaProvider ← intermediate native view
└── View (flex: 1) ← layout boundary
├── Header (opaque)
└── View (flex: 1) ← wrapper
└── FlatList
The algorithm searches self.subviews → finds SafeAreaProvider (not a ScrollView) → returns nil. The frame correction never runs.
Symptoms:
- First list item hidden behind the header (ScrollView at y=0 instead of below header)
- Bottom content clipped by exactly the header's height (ScrollView sized to full container instead of container minus header)
Workaround
Adding borderWidth: 0 to the wrapper View around the FlatList fixes both issues:
<View style={{ flex: 1, borderWidth: 0 }}>
<FlatList style={{ flex: 1 }} ... />
</View>Empirical testing showed that:
collapsable={false}alone: fixes top overlap but NOT bottom clippingbackgroundColoralone: fixes neitherborderWidth: 0(even without explicit borderColor): fixes both- Removing the wrapper View entirely: fixes bottom but reintroduces top overlap
The borderWidth: 0 appears to affect Yoga's box model calculation — defining border (even as 0) vs leaving it undefined changes how the flex container sizes within formSheet.
Suggested fix
Two possible approaches:
- Recursive/deeper ScrollView search: Make
childRCTScrollViewComponentAndContentContainertraverse deeper than 1 level to find the ScrollView through intermediate Views - Fix the underlying Fabric (0,0) placement: Resolve the TODO so the workaround algorithm isn't needed at all
Environment
- react-native-screens: 4.23.0
- react-native: 0.83.1
- Fabric (new architecture): enabled
- Platform: iOS
- Expo SDK: 53