React-native-reanimated-bottom-sheet: Present above [native] tab bar

Created on 17 Dec 2019  Â·  25Comments  Â·  Source: osdnk/react-native-reanimated-bottom-sheet

Hi, I’m using react native navigation – everything works great, except for the fact the bottom sheet is presented beneath the tab bar. Is there a way to make it show above it?

Thank you!

Most helpful comment

react-native-portalize the key guys!!!! Just wrap the tabnavigation in the Host component and after any place in the application wrap your bottomsheet component in a Portal component. Works like hell!!!!

All 25 comments

@zmnv I saw this issue, but I don‘t think it’s the same... This relates to a native tab bar, the issue you refer to relate to a non-native tab bar

@oferRounds what is a 'native tab bar'?

You mean bottom navigation bar in android?

Yes, but only on iOS

On iOS it’s called TabBar, and as react-native-navigation is based on the native iOS’ TabBar and Android‘s BottomNavigationBar, then it’s a different story compared to react-navigation JS solution

No matter iOS or Android. TabBar or BottomNavigationBar always inside <Navigation />.

You just need to place your bottom sheet ABOVE <Navigation /> component. Outside of Navigation Stack like in https://github.com/brentvatne/bottom-sheet-example/blob/master/App.tsx#L201

Well react-native-navigation works a bit differently, there’s no tag, not sure about how to place it outside. But let me check further...

@zmnv seems like there’s no good way to integrate your react-native-reanimated-bottom-sheet with react-native-navigation (see here: https://github.com/wix/react-native-navigation/issues/5789)

Perhaps we can change react-native-reanimated-bottom-sheet in the way that it will always (by default) present the sheet on the front? i.e., on its native implementation (on iOS, perhaps setting the windowLevel to Alert)

It doesn't matter which navigation you use...
react-native-reanimated-bottom-sheet is just view component

I see now that there is no native side... It does in the sense that it get presented bellow the tab bar... Perhaps it should use React Native‘s Modal component?

was manage to solve the issue by wrapping the component in a Modal component... Could be that this should be included in the component...

Hi, thank you for digging into this issue. Your problem seems to be caused entirely by react native navigation and that's why it has to be wrapped in a Modal component. Please use react navigation next time instead. I think that sums it up.

You can handle 'back' button on Android to close bottom sheet

AndroidBackHandler.tsx

import React, { PureComponent } from 'react';

import { BackHandler } from 'react-native';
import { NavigationEvents } from 'react-navigation';

type Props = {
    onBackPress: () => void,
};

export class AndroidBackHandler extends PureComponent<Props> {

    enabled = true;
    backHandler: any;

    componentDidMount() {
        this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
    }

    componentWillUnmount() {
        this.backHandler.remove();
    }

    handleBackPress = () => {
        if (this.enabled && this.props.onBackPress) {

            this.props.onBackPress();
            return true;
        }

        return false;
    }

    handleWillBlur = () => {
        this.enabled = false;
    }

    handleWillFocus = () => {
        this.enabled = true;
    }

    render() {
        return (
            <NavigationEvents
                onWillFocus={this.handleWillFocus}
                onWillBlur={this.handleWillBlur}
            />
        );
    }
}


@BrtqKr react-native-navigation is a popular library... Anyhow, wrap it in a modal solves the issue

@zmnv your last answer might be related to a different issue?

@oferRounds yes, it's good to put inside render to handle back navigation when component rendered.

image

So, when bottom sheet is opened hardware back button will hide it.
You can put tab slider and handle 'routing' (not actually navigation, but still...) inside bottom sheet.

ps: <FadeInOut /> don't mount children if visible = false.

@oferRounds indeed it is, but when you use react native navigation your bottomsheet is going to be rendered with a different hierarchy and is going to be displayed as you described.

@oferRounds Do you mean modal like https://reactnavigation.org/docs/en/modal.html ? With transparent background.

Care to share the boilerplate code used to achieve this @oferRounds ? I'm pretty sure there are other people facing this issue!

I finally solved my problem with react navigation tabs.

In my Stack screens, I created a new screen for de bottom sheet component:

<Stack.Screen name='MyScreen' component={MyScreenWithBottomSheet} options={modalAnimation} />

The modal animation, I got from here (https://reactnavigation.org/docs/stack-navigator/#transparent-modals):

const modalAnimation = {
  cardStyle: { backgroundColor: 'transparent' },
  cardOverlayEnabled: true,
  cardStyleInterpolator: ({ current: { progress } }) => ({
    cardStyle: {
      opacity: progress.interpolate({
        inputRange: [0, 0.5, 0.9, 1],
        outputRange: [0, 0.25, 0.7, 1]
      })
    },
    overlayStyle: {
      opacity: progress.interpolate({
        inputRange: [0, 1],
        outputRange: [0, 0.5],
        extrapolate: 'clamp'
      })
    }
  })
}

To hide de tab bar on this specific screen, I used this solution (https://github.com/react-navigation/react-navigation/issues/7677#issuecomment-591848526):

<Tabs.Screen
      name='Home'
      component={HomeStack}
      options={({ route }) => ({
        tabBarVisible: getTabBarVisible(route, 'Home')
      })}
/>
export const getTabBarVisible = (route, tabRoute) => {
  const routeName = route.state
    ? route.state.routes[route.state.index].name
    : route.params?.screen || tabRoute

  switch (tabRoute) {
    default:
    case 'Home': {
      switch (routeName) {
        case 'MyScreen':
        case 'SomeRouteToHideTabBar':
          return false
        default:
          return true
      }
    }
  }
}

I hope this can help.

@mstaicu sorry missed your comment!
Here it is:

<Modal
                useNativeDriver = { true }
                style = { styles.modal }
                isVisible = { this.state.isPresentingReactionsPickerBottomsheet }
                onBackButtonPress = { this.onReactionsPickerBottomSheetClose }
                onBackdropPress = { this.onReactionsPickerBottomSheetClose }
            >
                <BottomSheet
                    ref = { this.onGetReactionsPickerBottomSheetRef }
                    snapPoints = { [USERS_REACTIONS_BOTTOM_SHEET_INITIAL_HEIGHT, USERS_REACTIONS_BOTTOM_SHEET_INITIAL_HEIGHT / 2, 0] }
                    initialSnap = { 1 }
                    overdragResistanceFactor = { 8 }
                    renderContent = { this.renderReactionsPickerContent }
                    renderHeader = { this.reactionsPickerBottomSheetHeader }
                    enabledInnerScrolling = { true }

                    // listening to both events, as `closeStart` not called when half-way opened (called only when completely open),
                    // but we do want start the transition as early as possible
                    onCloseStart = { this.onReactionsPickerBottomSheetClose }
                    onCloseEnd = { this.onReactionsPickerBottomSheetClose }
                />
</Modal>

The sheet can be opened by setting isPresentingReactionsPickerBottomsheet to true, while closing it be listening the Modal’s callbacks: onBackButtonPress and onBackdropPress and running the same logic, but with false:

private onReactionsPickerBottomSheetClose = () => {
        this.setState({ isPresentingReactionsPickerBottomsheet: false })
    }

@mstaicu sorry missed your comment!
Here it is:

<Modal
                useNativeDriver = { true }
                style = { styles.modal }
                isVisible = { this.state.isPresentingReactionsPickerBottomsheet }
                onBackButtonPress = { this.onReactionsPickerBottomSheetClose }
                onBackdropPress = { this.onReactionsPickerBottomSheetClose }
            >
                <BottomSheet
                    ref = { this.onGetReactionsPickerBottomSheetRef }
                    snapPoints = { [USERS_REACTIONS_BOTTOM_SHEET_INITIAL_HEIGHT, USERS_REACTIONS_BOTTOM_SHEET_INITIAL_HEIGHT / 2, 0] }
                    initialSnap = { 1 }
                    overdragResistanceFactor = { 8 }
                    renderContent = { this.renderReactionsPickerContent }
                    renderHeader = { this.reactionsPickerBottomSheetHeader }
                    enabledInnerScrolling = { true }

                    // listening to both events, as `closeStart` not called when half-way opened (called only when completely open),
                    // but we do want start the transition as early as possible
                    onCloseStart = { this.onReactionsPickerBottomSheetClose }
                    onCloseEnd = { this.onReactionsPickerBottomSheetClose }
                />
</Modal>

The sheet can be opened by setting isPresentingReactionsPickerBottomsheet to true, while closing it be listening the Modal’s callbacks: onBackButtonPress and onBackdropPress and running the same logic, but with false:

private onReactionsPickerBottomSheetClose = () => {
        this.setState({ isPresentingReactionsPickerBottomsheet: false })
    }

Is it swipable with modal?
I'm using react-native-modal with react-native-reanimated-bottom-sheet and its not!

@Abdulaev, as far as I know, react-native-modal is a wrapper around the Modal component exposed by react-native, which seeks to add missing functionality. It does not solve any of the existing issues regarding the usage of the bottom sheet in the modal. The main issue, for me at least, is the fact that the fix introduced in this pull request does not work when the bottom sheet is used inside a modal which is then rendered on a screen that is rendering lots of times. The result is that it auto dismisses the modal that contains the bottom sheet. I will respond to this thread if I have any updates regarding fixes or workarounds

@zmnv thanks – no, I mean https://github.com/react-native-community/react-native-modal

by wrapping BottomSheet by Modal - react-native-reanimated-bottom-sheet gesture is not working like dragging and droping.

react-native-portalize the key guys!!!! Just wrap the tabnavigation in the Host component and after any place in the application wrap your bottomsheet component in a Portal component. Works like hell!!!!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gabrielmoncea picture gabrielmoncea  Â·  6Comments

Saad-Bashar picture Saad-Bashar  Â·  6Comments

praneeth-hiver picture praneeth-hiver  Â·  4Comments

ardyfeb picture ardyfeb  Â·  5Comments

fadhlisulaimi picture fadhlisulaimi  Â·  5Comments