Skip to content

A high performance, beautiful and fully customizable curved bottom navigation bar for React Native.

License

Notifications You must be signed in to change notification settings

hoaphantn7604/react-native-curved-bottom-bar

Repository files navigation

react-native-curved-bottom-bar

A high performance, beautiful and fully customizable curved bottom navigation bar for React Native. Implemented using react-native-svg and @react-navigation/bottom-tabs.

Getting started

npm install react-native-curved-bottom-bar --save

or

yarn add react-native-curved-bottom-bar

Now we need to install react-native-svg and @react-navigation/bottom-tabs.

Demo

CurvedBottomBar.Navigator

Props Params isRequire Description
type 'DOWN' or 'UP' Yes Type of the center tab item, downward curve or upward curve
circlePosition 'CENTER' or 'LEFT' or 'RIGHT' No Position of circle button
initialRouteName String Yes The name of the route to render on first load of the navigator
tabBar ({ routeName, selectedTab, navigate }) => JSX.Element Yes Function that returns a React element to display as the tab bar
renderCircle ({ routeName, selectedTab, navigate }) => JSX.Element Yes Function that returns a React element to display as the center tab item
circleWidth Number No Customize width of the center tab item. Minimum is 50px and Maximum is 60px
style ViewStyle No Styling for container view
shadowStyle ViewStyle No Styling for shadow view.
width Number No Customize width for container view
height Number No Customize height for container view, Minimum is 50px and Maximum is 90px
borderTopLeftRight Boolean No Border radius top left and top right of container view
bgColor String No Background color of container view

CurvedBottomBar.Screen

Props Params isRequire Description
name String Yes Name of the route to jump to
position 'LEFT' or 'RIGHT' or 'CIRCLE' Yes Set position of tabbar icon to the left or right of the circle button. Use type "CIRCLE" only when you want the circle button is a tabview
component (props) => JSX.Element Yes Screen params to merge into the destination route

API

Function Params Description
setVisible Boolean Used to hide/show the tab bar. Ex: ref.current.setVisible(false)

Use in Expo

  import React from 'react';
  import {
    Alert,
    Animated,
    StyleSheet,
    TouchableOpacity,
    View,
  } from 'react-native';
  import { CurvedBottomBarExpo } from 'react-native-curved-bottom-bar';
  import Ionicons from '@expo/vector-icons/Ionicons';
  import { NavigationContainer } from '@react-navigation/native';

  export default function App() {
    const _renderIcon = (routeName, selectedTab) => {
      let icon = '';

      switch (routeName) {
        case 'title1':
          icon = 'ios-home-outline';
          break;
        case 'title2':
          icon = 'settings-outline';
          break;
      }

      return (
        <Ionicons
          name={icon}
          size={25}
          color={routeName === selectedTab ? 'black' : 'gray'}
        />
      );
    };
    const renderTabBar = ({ routeName, selectedTab, navigate }) => {
      return (
        <TouchableOpacity
          onPress={() => navigate(routeName)}
          style={{
            flex: 1,
            alignItems: 'center',
            justifyContent: 'center',
          }}>
          {_renderIcon(routeName, selectedTab)}
        </TouchableOpacity>
      );
    };

    return (
      <View style={{ flex: 1 }}>
        <NavigationContainer>
          <CurvedBottomBarExpo.Navigator
            style={styles.bottomBar}
            height={55}
            circleWidth={50}
            bgColor="white"
            initialRouteName="title1"
            borderTopLeftRight
            renderCircle={({ selectedTab, navigate }) => (
              <Animated.View style={styles.btnCircle}>
                <TouchableOpacity
                  style={{
                    flex: 1,
                    justifyContent: 'center',
                  }}
                  onPress={() => Alert.alert('Click Action')}>
                  <Ionicons name={'apps-sharp'} color="gray" size={25} />
                </TouchableOpacity>
              </Animated.View>
            )}
            tabBar={renderTabBar}>
            <CurvedBottomBarExpo.Screen
              name="title1"
              position="LEFT"
              component={() => (
                <View style={{ backgroundColor: '#BFEFFF', flex: 1 }} />
              )}
            />
            <CurvedBottomBarExpo.Screen
              name="title2"
              component={() => (
                <View style={{ backgroundColor: '#FFEBCD', flex: 1 }} />
              )}
              position="RIGHT"
            />
          </CurvedBottomBarExpo.Navigator>
        </NavigationContainer>
      </View>
    );
  };

  export const styles = StyleSheet.create({
    container: {
      flex: 1,
      padding: 20,
    },
    button: {
      marginVertical: 5,
    },
    bottomBar: {},
    btnCircle: {
      width: 60,
      height: 60,
      borderRadius: 35,
      alignItems: 'center',
      justifyContent: 'center',
      backgroundColor: 'white',
      padding: 10,
      shadowColor: '#000',
      shadowOffset: {
        width: 0,
        height: 0.5,
      },
      shadowOpacity: 0.2,
      shadowRadius: 1.41,
      elevation: 1,
      bottom: 30,
    },
    imgCircle: {
      width: 30,
      height: 30,
      tintColor: 'gray',
    },
    img: {
      width: 30,
      height: 30,
    },
  });

Use in RN CLI

  import React from 'react';
  import {
    Alert,
    Animated,
    StyleSheet,
    TouchableOpacity,
    View,
  } from 'react-native';
  import { CurvedBottomBar } from 'react-native-curved-bottom-bar';
  import Ionicons from 'react-native-vector-icons/Ionicons';
  import { NavigationContainer } from '@react-navigation/native';

  export default function App() {
    const _renderIcon = (routeName, selectedTab) => {
      let icon = '';

      switch (routeName) {
        case 'title1':
          icon = 'ios-home-outline';
          break;
        case 'title2':
          icon = 'settings-outline';
          break;
      }

      return (
        <Ionicons
          name={icon}
          size={25}
          color={routeName === selectedTab ? 'black' : 'gray'}
        />
      );
    };
    const renderTabBar = ({ routeName, selectedTab, navigate }) => {
      return (
        <TouchableOpacity
          onPress={() => navigate(routeName)}
          style={{
            flex: 1,
            alignItems: 'center',
            justifyContent: 'center',
          }}>
          {_renderIcon(routeName, selectedTab)}
        </TouchableOpacity>
      );
    };

    return (
      <View style={{ flex: 1 }}>
        <NavigationContainer>
          <CurvedBottomBar.Navigator
            type="UP"
            style={styles.bottomBar}
            height={55}
            circleWidth={50}
            bgColor="white"
            initialRouteName="title1"
            borderTopLeftRight
            renderCircle={({ selectedTab, navigate }) => (
              <Animated.View style={styles.btnCircleUp}>
                <TouchableOpacity
                  style={{
                    flex: 1,
                    justifyContent: 'center',
                  }}
                  onPress={() => Alert.alert('Click Action')}>
                  <Ionicons name={'apps-sharp'} color="gray" size={25} />
                </TouchableOpacity>
              </Animated.View>
            )}
            tabBar={renderTabBar}>
            <CurvedBottomBar.Screen
              name="title1"
              position="LEFT"
              component={() => (
                <View style={{ backgroundColor: '#BFEFFF', flex: 1 }} />
              )}
            />
            <CurvedBottomBar.Screen
              name="title2"
              component={() => (
                <View style={{ backgroundColor: '#FFEBCD', flex: 1 }} />
              )}
              position="RIGHT"
            />
          </CurvedBottomBar.Navigator>
        </NavigationContainer>
      </View>
    );
  };

  export const styles = StyleSheet.create({
    container: {
      flex: 1,
      padding: 20,
    },
    button: {
      marginVertical: 5,
    },
    bottomBar: {},
    btnCircleUp: {
      width: 60,
      height: 60,
      borderRadius: 30,
      alignItems: 'center',
      justifyContent: 'center',
      backgroundColor: '#E8E8E8',
      bottom: 18,
      shadowColor: '#000',
      shadowOffset: {
        width: 0,
        height: 1,
      },
      shadowOpacity: 0.2,
      shadowRadius: 1.41,
      elevation: 1,
    },
    imgCircle: {
      width: 30,
      height: 30,
      tintColor: 'gray',
    },
    img: {
      width: 30,
      height: 30,
    },
  });