Skip to content

Commit 7ac4bb6

Browse files
authored
fix(Android, FormSheet): Prevent BottomSheetBehavior override when using FormSheets (#3912)
## Description When `headerTransparent: true` is configured, `setToolbarTranslucent(true)` is invoked. Previously, this method overwrote the behavior with `null`. For FormSheet presentation, this unintentionally destroyed the BottomSheetBehavior. When `handleInsetsUpdateAndNotifyTransition` was invoked, the call to `screen.sheetBehavior!!` was crashing with `NullPointerException`. > [!NOTE] > Native header isn't supported on Android currently: https://reactnavigation.org/docs/native-stack-navigator/#android-specific-limitations - this issue aims only to resolve the crash, but for now we're not planning to add this feature for v4. > [!NOTE] > I deliberately removed SAV from the original repro, as it seems to be causing another issue: software-mansion/react-native-screens-labs#968 - adding it to my queue Closes #3910 ## Changes - Guarded `setToolbarTranslucent` to only modify the layout behavior if we're not in the FormSheet presentation. ## Before & after - visual documentation | Before | After | | --- | --- | | <video src="https://github.com/user-attachments/assets/7ad82901-f3ca-4dbd-9f81-d117b46cfce3" /> | <video src="https://github.com/user-attachments/assets/03a630d1-2224-4788-8697-621c238d04f1" /> | ## Test plan Added Test3910 ## Checklist - [ ] Included code example that can be used to test this change. - [ ] For visual changes, included screenshots / GIFs / recordings documenting the change. - [ ] For API changes, updated relevant public types. - [ ] Ensured that CI passes
1 parent 6ae17e0 commit 7ac4bb6

3 files changed

Lines changed: 65 additions & 3 deletions

File tree

android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import com.swmansion.rnscreens.events.ScreenDismissedEvent
3737
import com.swmansion.rnscreens.ext.recycle
3838
import com.swmansion.rnscreens.stack.views.ScreensCoordinatorLayout
3939
import com.swmansion.rnscreens.utils.DeviceUtils
40+
import com.swmansion.rnscreens.utils.RNSLog
4041
import com.swmansion.rnscreens.utils.resolveBackgroundColor
4142
import kotlin.math.max
4243

@@ -128,9 +129,14 @@ class ScreenStackFragment :
128129

129130
override fun setToolbarTranslucent(translucent: Boolean) {
130131
if (isToolbarTranslucent != translucent) {
131-
val params = screen.layoutParams
132-
(params as CoordinatorLayout.LayoutParams).behavior =
133-
if (translucent) null else ScrollingViewBehavior()
132+
// FormSheet is using BottomSheetBehavior which shouldn't be overwritten.
133+
if (!screen.usesFormSheetPresentation()) {
134+
val params = screen.layoutParams
135+
(params as CoordinatorLayout.LayoutParams).behavior =
136+
if (translucent) null else ScrollingViewBehavior()
137+
} else {
138+
RNSLog.w(TAG, "Skipping behavior update: Cannot override BottomSheetBehavior on a FormSheet.")
139+
}
134140
isToolbarTranslucent = translucent
135141
}
136142
}
@@ -583,4 +589,8 @@ class ScreenStackFragment :
583589
}
584590
return bottomSheetWindowInsetListenerChain!!
585591
}
592+
593+
companion object {
594+
private const val TAG = "ScreenStackFragment"
595+
}
586596
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React from 'react';
2+
import { Button, Text, View } from 'react-native';
3+
import { NavigationContainer } from '@react-navigation/native';
4+
import {
5+
createNativeStackNavigator,
6+
NativeStackScreenProps,
7+
} from '@react-navigation/native-stack';
8+
import { SafeAreaView } from 'react-native-screens/experimental';
9+
10+
export type RootStackParamList = {
11+
Home: undefined;
12+
Sheet: undefined;
13+
};
14+
15+
const Stack = createNativeStackNavigator<RootStackParamList>();
16+
17+
const HomeScreen = ({
18+
navigation,
19+
}: NativeStackScreenProps<RootStackParamList, 'Home'>) => (
20+
<SafeAreaView edges={{ top: true, bottom: true }}>
21+
<Button
22+
title="Open form sheet"
23+
onPress={() => navigation.navigate('Sheet')}
24+
/>
25+
</SafeAreaView>
26+
);
27+
28+
const SheetScreen = () => (
29+
<View style={{ height: 250 }}>
30+
<Text>This is a form sheet.</Text>
31+
</View>
32+
);
33+
34+
const App = () => (
35+
<NavigationContainer>
36+
<Stack.Navigator>
37+
<Stack.Screen name="Home" component={HomeScreen} />
38+
<Stack.Screen
39+
name="Sheet"
40+
component={SheetScreen}
41+
options={{
42+
presentation: 'formSheet',
43+
sheetAllowedDetents: 'fitToContents',
44+
headerTransparent: true,
45+
}}
46+
/>
47+
</Stack.Navigator>
48+
</NavigationContainer>
49+
);
50+
51+
export default App;

apps/src/tests/issue-tests/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ export { default as Test3833 } from './Test3833';
191191
export { default as Test3835 } from './Test3835';
192192
export { default as Test3867 } from './Test3867';
193193
export { default as Test3885 } from './Test3885';
194+
export { default as Test3910 } from './Test3910';
194195
export { default as TestScreenAnimation } from './TestScreenAnimation';
195196
// The following test was meant to demo the "go back" gesture using Reanimated
196197
// but the associated PR in react-navigation is currently put on hold

0 commit comments

Comments
 (0)