0

This is BottomTabNavigator.js file:

const BottomTab = createBottomTabNavigator();
const { height } = Dimensions.get('window');
const SNAP_TOP = height + StatusBar.currentHeight;
const SNAP_BOTTOM = 54 + 50;
    
const BottomTabNavigator = () => {
    const yValue = useSharedValue(SNAP_BOTTOM);
    return (
        <>
          {/* this component contain all screen and bottom tab navigator */}
            <BottomTab.Navigator
                tabBar={(props) => <BottomTabBar {...props} yValue={yValue} snapTop={SNAP_TOP} snapBottom={SNAP_BOTTOM} />}
                initialRouteName="Home"   // "Home" initial screen to render 
                backBehavior="history"
                screenOptions={{
                    headerShown: false,
                    tabBarHideOnKeyboard: true,
                }}
            >
                <BottomTab.Screen
                    name="Home"
                    component={Home}
                    options={{
                        tabBarLabel: "Home",
                        // we are importing focused as well as size from BottomTabBar.js file
                        tabBarIcon: ({ color }) => <HomeIcon height={icon_size} width={icon_size} fill={color} />,
                    }}
                />
                <BottomTab.Screen
                    name="Search"
                    component={Search}
                    options={{
                        tabBarLabel: "Search",
                        tabBarIcon: ({ color }) => <SearchIcon height={icon_size} width={icon_size} fill={color} />
                    }}
                />
            </BottomTab.Navigator>
            {/* this component contains the mini player & player page */}
            <SlidingBottom yValue={yValue} snapTop={SNAP_TOP} snapBottom={SNAP_BOTTOM} />
        </>
    );
}

export default BottomTabNavigator;

This is SlidingBottom.js file:

const { height } = Dimensions.get('window');
const SNAP_TOP = height + StatusBar.currentHeight;
const SNAP_BOTTOM = 54 + 50;
const config = {
    damping: 15,
    mass: 1,
    stiffness: 120,
    restSpeedThreshold: 0.1,
    restDisplacementThreshold: 0.1,
}

const SlidingBottom = ({ yValue }) => {
    // const yValue = useSharedValue(SNAP_BOTTOM);
    const prevYValue = useSharedValue(SNAP_BOTTOM);
    const pan = Gesture.Pan().minDistance(1).onBegin(() => {
        prevYValue.value = yValue.value;
    }).onUpdate((evevnt) => {
        yValue.value = clamp(
            prevYValue.value - evevnt.translationY,
            SNAP_BOTTOM,
            SNAP_TOP
        );
    }).onEnd((evevnt) => {
        yValue.value = withSpring(
            snapPoints(
                clamp(
                    prevYValue.value - evevnt.translationY,
                    SNAP_BOTTOM,
                    SNAP_TOP
                ),
                -evevnt.velocityY,
                [SNAP_BOTTOM, SNAP_TOP]),
            config
        );
    }).runOnJS(true);

    const yStyle = useAnimatedStyle(() => ({
        height: yValue.value
    }))

    const oStyle = useAnimatedStyle(() => ({
        opacity: interpolate(yValue.value, [SNAP_TOP, SNAP_BOTTOM], [0, 1], Extrapolation.CLAMP)
    }))

    return (
        <GestureDetector gesture={pan}>
            <Animated.View style={[yStyle, { position: "absolute", left: 0, right: 0, bottom: 0, backgroundColor: "red", borderColor: "purple", borderWidth: 1, zIndex: layout_schema.zIndexForSlidingBottom }]}>
                <Animated.View style={[oStyle, { height: 50, backgroundColor: "mediumseagreen" }]} />
                <Player />
            </Animated.View>
        </GestureDetector>
    );
}

export default SlidingBottom;

This is BottomTabBar.js file:

import { useEffect, useState, useMemo } from "react";
import { Dimensions, Keyboard, Text, View, TouchableOpacity, StatusBar, StyleSheet } from "react-native";
import { useTheme } from "@react-navigation/native";
import Animated, { Extrapolation, interpolate, useSharedValue, useAnimatedStyle, withTiming, Easing } from "react-native-reanimated";
// import constants
import { color_schema, icon_size, layout_schema } from "../../constants/constants";

const { height } = Dimensions.get('window');

const BottomTabBar = ({ state, descriptors, navigation, yValue, snapTop, snapBottom }) => {    
    // using this hook we can get all colors used by react navigation 
    // as hooks can be only called inside function that's why we cannot declare all styles in StyleSheet
    const { colors } = useTheme();

    // this is used to check if keyboard is visible or not
    const [keyboardVisible, setKeyboardVisible] = useState(true);

    useEffect(() => {
        const keyboardDidShowListener = Keyboard.addListener("keyboardDidShow", () => {
            //Whenever keyboard did show make it don't visible
            setKeyboardVisible(false);
        });
        const keyboardDidHideListener = Keyboard.addListener("keyboardDidHide", () => {
            setKeyboardVisible(true);
        });
        return () => {
            keyboardDidShowListener.remove();
            keyboardDidHideListener.remove();
        };
    }, [])

    const bStyle = useAnimatedStyle(() => ({
        // height: interpolate(yValue.value, [snapTop, snapBottom], [0, 54], Extrapolation.CLAMP), then add bottom: 0 in styles.bottom
        bottom: interpolate(yValue.value, [snapTop, snapBottom], [-54, 0], Extrapolation.CLAMP)
    }));

    return (
        keyboardVisible &&
        <Animated.View style={[styles.bottom, bStyle, { backgroundColor: colors.card }]}>
            {
                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 = useMemo(() => state.index === index, [state.index]);

                    {/* function for onPress event */ }
                    const onPress = () => {
                        const event = navigation.emit({
                            type: "tabPress",
                            target: route.key,
                        });
                        if (!isFocused && !event.defaultPrevented) {
                            navigation.navigate(route.name);
                        }
                    };

                    {/* function for onLongPress event */ }
                    const onLongPress = () => {
                        const event = navigation.emit({
                            type: "tabLongPress",
                            target: route.key,
                        });
                        if (!isFocused && !event.defaultPrevented) {
                            navigation.navigate(route.name);
                        }
                    };

                    {/* setting color for icon and text */ }
                    const color = isFocused ? color_schema.focused_header_color : color_schema.unfocused_header_color
                    const widthSharedValue = useSharedValue(isFocused ? "100%" : "0%");

                    useEffect(() => {
                        widthSharedValue.value = withTiming(isFocused ? "100%" : "0%", {
                            duration: 300,
                            easing: Easing.bounce,
                        })
                    }, [state.index])

                    const animatedWidth = useAnimatedStyle(() => {
                        'worklet';
                        return {
                            width: widthSharedValue.value,
                        };
                    })

                    return (
                        <TouchableOpacity
                            key={route.key} // using route.key for unique keys but we can use index also
                            // accessibilityRole="button"
                            accessibilityState={isFocused ? { selected: true } : {}}
                            accessibilityLabel={options.tabBarAccessibilityLabel}
                            testID={options.tabBarTestID}
                            onPress={onPress}
                            onLongPress={onLongPress}
                        >
                            <View style={styles.navItem}>
                            {/* this view contains icon */}
                            <View>
                                    {options.tabBarIcon({ color, focused: isFocused, size: icon_size })}
                                </View>
                            {/* this view contains label */}
                            <View style={styles.labelContainer}>
                                    <Text style={[styles.label, { color }]}>{label}</Text>
                                </View>
                            {/* this view contains undedrline */}
                            <Animated.View style={[styles.underline, animatedWidth]} />
                            </View>
                        </TouchableOpacity>
                    );
                })
            }
        </Animated.View>
    );
}

const styles = StyleSheet.create({
    bottom: {
        borderTopColor: color_schema.border_bottom_color,
        borderTopWidth: 1,
        flexDirection: "row",
        justifyContent: "space-evenly",
        alignItems: "center",
        paddingTop: 5,
        paddingBottom: 2,
        position: "absolute",
        left: 0,
        right: 0,
        zIndex: layout_schema.zIndexForBottomTabNavigator,
        height: 54,
        overflow: "hidden",
    },
    navItem: {
        display: "flex",
        flexDirection: "column",
        justifyContent: "center",
        alignItems: "center",
        width: "100%",
    },
    labelContainer: {
        marginTop: -1,
        marginBottom: 1,
        marginHorizontal: "auto",
        width: "auto",
    },
    label: {
        fontSize: 10,
        fontWeight: "400",
    },
    underline: {
        backgroundColor: color_schema.focused_header_color,
        borderRadius: 2,
        height: 2,
    },
});

export default BottomTabBar;

This code works fine in RN 0.74, but not in RN 0.76. In RN 0.76, height is not changing on initial render, but when I click on some other tab, it works fine. I tried to debug but cannot find the reason.
Also, the reason I want to jump to RN 0.76 it due to its new architecture.
I know there are other npm packages like this, but I am skeptical in using those pacakges.
Kindly help me react-native community.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.