react-nativereact-native-navigationreact-navigation-stack

How to display a bottom sheet from React navigation BottomTabNavigator?


My solution


How to display a bottom sheet from React navigation BottomTabNavigator?

React navigation BottomTabNavigator

I want to display a reanimated-bottom-sheet when I click the tabBarIcon (maybe such as button adding in picture) instead of a component.

I'm using

<Tab.Screen
    name={Name.name_add_application}
    component={Add}
    options={{
      tabBarIcon: ({focused}) => (
        <Image source={TK_Add} resizeMode="contain" style={styles.addBtn} />
      ),
      tabBarButton: props => <CustomTabButton {...props} />,
    }}
    listeners={({navigation}) => ({
      tabPress: e => {
        e.preventDefault();
        navigation.navigate('CreateNew');
      },
    })}
  />

in const Tab = createBottomTabNavigator(); and

<MainStack.Group
  screenOptions={{
    headerShown: false,
    cardStyle: {backgroundColor: 'rgba(0, 0, 0, 0)'},
    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, 0.5],
          outputRange: [0, 0.25],
          extrapolate: 'clamp',
        }),
      },
    }),
  }}
  mode="modal">
  <MainStack.Screen
    name="CreateNew"
    component={CreateNew}
    options={{
      animationEnabled: true,
      presentation: 'transparentModal',
    }}
  />
</MainStack.Group>

in const MainStack = createStackNavigator(); to open a Modal component.

But it have a little bit lag, show a white background for about 0.01s and can't scroll (I don't want to use this method anymore).


Solution

  • This is my solution

    function MyTabBar({ state, descriptors, navigation }) {
      const tabRef = React.createRef();
      const fall = new Animated.Value(1);
    
      const renderInner = () => (
        <View style={styles.panel}>{/* ButtomSheet body */}</View>
      );
    
      const renderHeader = () => (
        <View style={styles.header}>{/* ButtomSheet header */}</View>
      );
    
      return (
        <View style={{ flexDirection: "row" }}>
          <BottomSheet
            ref={tabRef}
            snapPoints={[CONST.HEIGHT * 0.5, 0]}
            renderContent={renderInner}
            renderHeader={renderHeader}
            initialSnap={1}
            callbackNode={fall}
            enabledGestureInteraction={true}
          />
          {state.routes.map((route, index) => {
            const { options } = descriptors[route.key];
            // const label =
            //   options.tabBarLabel !== undefined
            //     ? options.tabBarLabel
            //     : options.title !== undefined
            //     ? options.title
            //     : route.name;
    
            const isFocused = state.index === index;
    
            const onPress = () => {
              switch (index) {
                // add button's case ( which I want to show bottom sheet)
                case 2:
                  tabRef.current.snapTo(0);
                  break;
    
                default:
                  const event = navigation.emit({
                    type: "tabPress",
                    target: route.key,
                  });
    
                  if (!isFocused && !event.defaultPrevented) {
                    navigation.navigate(route.name);
                  }
                  break;
              }
            };
    
            const onLongPress = () => {
              navigation.emit({
                type: "tabLongPress",
                target: route.key,
              });
            };
    
            const TabIcon = () => {
              // custom your tab
            };
    
            return (
              <Pressable
                accessibilityRole="button"
                accessibilityState={isFocused ? { selected: true } : {}}
                accessibilityLabel={options.tabBarAccessibilityLabel}
                testID={options.tabBarTestID}
                onPress={onPress}
                onLongPress={onLongPress}
                style={{
                  flex: 1,
                  alignItems: "center",
                  backgroundColor: "#fff",
                }}
              >
                <TabIcon />
                {/* {label} */}
              </Pressable>
            );
          })}
        </View>
      );
    }
    

    in const Tab = createBottomTabNavigator();

    <Tab.Navigator
      tabBar={props => <MyTabBar {...props} />}
      screenOptions={{headerShown: false}}>
      <Tab.Screen name={/* screen name */} component={/* compo */} />
      {/* more screens */}
    </Tab.Navigator>