Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Android): going back on fabric with removeClippedSubviews #2495

Merged
merged 14 commits into from
Nov 15, 2024
14 changes: 8 additions & 6 deletions android/src/main/java/com/swmansion/rnscreens/Screen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ import com.facebook.react.uimanager.PixelUtil
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.UIManagerModule
import com.facebook.react.uimanager.events.EventDispatcher
import com.facebook.react.views.scroll.ReactHorizontalScrollView
import com.facebook.react.views.scroll.ReactScrollView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.shape.CornerFamily
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel
import com.swmansion.rnscreens.events.HeaderHeightChangeEvent
import com.swmansion.rnscreens.events.SheetDetentChangedEvent
import com.swmansion.rnscreens.ext.isInsideScrollViewWithRemoveClippedSubviews
import java.lang.ref.WeakReference

@SuppressLint("ViewConstructor") // Only we construct this view, it is never inflated.
Expand Down Expand Up @@ -399,11 +400,12 @@ class Screen(
startTransitionRecursive(child.toolbar)
}
if (child is ViewGroup) {
// The children are miscounted when there's a FlatList with
// removeClippedSubviews set to true (default).
// We add a simple view for each item in the list to make it work as expected.
// See https://github.com/software-mansion/react-native-screens/pull/2383
if (child.isInsideScrollViewWithRemoveClippedSubviews()) {
// The children are miscounted when there's removeClippedSubviews prop
// set to true (which is the default for FlatLists).
// Unless the child is a ScrollView it's safe to assume that it's true
// and add a simple view for each possibly clipped item to make it work as expected.
// See https://github.com/software-mansion/react-native-screens/pull/2495
if (child !is ReactScrollView && child !is ReactHorizontalScrollView) {
for (j in 0 until child.childCount) {
child.addView(View(context))
}
Expand Down
24 changes: 0 additions & 24 deletions android/src/main/java/com/swmansion/rnscreens/ext/ViewExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ package com.swmansion.rnscreens.ext
import android.graphics.drawable.ColorDrawable
import android.view.View
import android.view.ViewGroup
import com.facebook.react.views.scroll.ReactHorizontalScrollView
import com.facebook.react.views.scroll.ReactScrollView
import com.swmansion.rnscreens.ScreenStack

internal fun View.parentAsView() = this.parent as? View

Expand Down Expand Up @@ -33,24 +30,3 @@ internal fun View.maybeBgColor(): Int? {
}
return null
}

internal fun View.isInsideScrollViewWithRemoveClippedSubviews(): Boolean {
if (this is ReactHorizontalScrollView || this is ReactScrollView) {
return false
}
var parentView = this.parent
while (parentView is ViewGroup && parentView !is ScreenStack) {
when (parentView) {
is ReactHorizontalScrollView -> {
return parentView.removeClippedSubviews
}
is ReactScrollView -> {
return parentView.removeClippedSubviews
}
else -> {
parentView = parentView.parent
}
}
}
return false
}
61 changes: 59 additions & 2 deletions apps/src/tests/Test2282.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
ViewProps,
Image,
FlatListProps,
findNodeHandle,
} from 'react-native';

enableScreens(true);
Expand All @@ -34,13 +35,52 @@ function Home({ navigation }: any) {

function ListScreen() {
return (
<View style={{ flex: 1, backgroundColor: 'slateblue' }}>
<View
style={{ flex: 1, backgroundColor: 'slateblue' }}
removeClippedSubviews>
<ParentFlatlist />
<View removeClippedSubviews>
<View style={{ backgroundColor: 'pink', width: '100%', height: 50 }} />
</View>
<ParentFlatlist horizontal />
</View>
);
}

function ListScreenSimplified({secondVisible}: {secondVisible?: (visible: boolean) => void}) {
const containerRef = React.useRef<View>(null);
const innerViewRef = React.useRef<View>(null);
const childViewRef = React.useRef<View>(null);

React.useEffect(() => {
if (containerRef.current != null) {
const tag = findNodeHandle(containerRef.current);
console.log(`Container has tag [${tag}]`);
}
if (innerViewRef.current != null) {
const tag = findNodeHandle(innerViewRef.current);
console.log(`InnerView has tag [${tag}]`);
}
if (childViewRef.current != null) {
const tag = findNodeHandle(childViewRef.current);
console.log(`ChildView has tag [${tag}]`);
}
}, [containerRef.current, innerViewRef.current, childViewRef.current]);

return (
<View
ref={containerRef}
style={{ flex: 1, backgroundColor: 'slateblue', overflow: 'hidden' }}
removeClippedSubviews={false}>
<View ref={innerViewRef} removeClippedSubviews style={{ height: '100%' }}>
<View ref={childViewRef} style={{ backgroundColor: 'pink', width: '100%', height: 50 }} removeClippedSubviews={false}>
{secondVisible && (<Button title='Hide second' onPress={() => secondVisible(false)} />)}
</View>
</View>
</View>
);
}

function ParentFlatlist(props: Partial<FlatListProps<number>>) {
return (
<FlatList
Expand Down Expand Up @@ -100,12 +140,29 @@ export default function App(): React.JSX.Element {
<NavigationContainer>
<Stack.Navigator screenOptions={{ animation: 'slide_from_right' }}>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="List" component={ListScreen} />
<Stack.Screen name="List" component={ListScreenSimplified}/>
</Stack.Navigator>
</NavigationContainer>
);
}

export function AppSimple(): React.JSX.Element {
const [secondVisible, setSecondVisible] = React.useState(false);

return (
<View style={{ flex: 1, backgroundColor: 'lightsalmon' }}>
{!secondVisible && (
<View style={{ flex: 1, backgroundColor: 'lightblue' }} >
<Button title='Show second' onPress={() => setSecondVisible(true)} />
</View>
)}
{secondVisible && (
<ListScreenSimplified secondVisible={setSecondVisible} />
)}
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
Expand Down
Loading