react-navigation-bottom-sheet
react-navigation-bottom-sheet copied to clipboard
Functionality for rendering a single modal over a stack
Hi there, thank you for the great library, it is a great abstraction over the bottom sheet library for routing!
An issue I came across was needing to show a single bottom sheet in a transparent modal over a screen like this:
const Stack = createNativeStackNavigator();
const BottomSheet = createBottomSheetNavigator();
function MyModal() {
return (
<BottomSheet.Navigator>
<BottomSheet.Screen
component={MyModalComponent}
name="MyModal"
options={MyModalComponent.options}
/>
</BottomSheet.Navigator>
);
}
export function HomeStack() {
return (
<Stack.Navigator>
<Stack.Screen
component={HomeScreen}
name="Home"
/>
<Stack.Group screenOptions={{ presentation: "transparentModal" }}>
<Stack.Screen
component={MyModal}
name="MyModalBottomSheet"
/>
</Stack.Group>
</Stack.Navigator>
);
}
If my HomeStack has lots of other screens that I don't want to be shown as bottom sheets then the above I think is the only option. But this library seems to require that the first screen in the bottom sheet navigator be your app content, not a modal. I'm not sure the best solution but I've added the below patch with a firstScreenIsModal option to always show the bottom sheet provider if true.
I'm happy to submit a PR with the below patch if you think it is a good approach but wanted to get your thoughts on it first!
Patch
// in BottomSheetView.tsx
+ const firstScreen = descriptors[state.routes[0].key];
+ const { firstScreenIsModal = false } = firstScreen.options
+
// Avoid rendering provider if we only have one screen.
- const shouldRenderProvider = React.useRef(false);
+ const shouldRenderProvider = React.useRef(firstScreenIsModal);
shouldRenderProvider.current =
shouldRenderProvider.current || state.routes.length > 1;
- const firstScreen = descriptors[state.routes[0].key];
return (
<>
- {firstScreen.render()}
+ {firstScreenIsModal ? null : firstScreen.render()}
{shouldRenderProvider.current && (
<BottomSheetModalProvider>
- {state.routes.slice(1).map((route) => {
+ {state.routes.slice(firstScreenIsModal ? 0 : 1).map((route) => {
const { options, navigation, render } = descriptors[route.key];
I was thinking maybe passing the app content as a navigator prop instead of using the first screen would be better. This should solve this case.
Yep that would work as well!
I'm not sure I'll have time to put together a PR for a little while as my solution above has solved my immediate need but will come back to it later and let you know before I try implementing your idea!