Creating an animatable bottom bar with Animated and  React Navigation in React Native
Tags: reactnative, reactnavigation
Published: 2020-02-02 17:08:01

This tutorial is essentially to show how you can apply animations to the bottom bar view when you press any of the tab buttons in a react native app. I recently saw a UI design concept illustrating this and with a few tweaks from the docs from react-navigation, I was able to achieve this. Note that you should have prior knowledge of creating basic animations in react native as this is not a tutorial to get started with it. We'll need *react-navigation* and *react-navigation-tabs* for this project. The full code can be viewed [here](https://github.com/echoeyecodes/animated-bottom-bar)

Here's a gif showing what we'll be building:
![video](https://dev-to-uploads.s3.amazonaws.com/i/okar893ukpf0swoto1y7.gif)

First we import our necessary files in the **App.js** file with the following code:
```
import React, {useState} from 'react'
import {View, Dimensions, Animated} from 'react-native'
import {createBottomTabNavigator, BottomTabBar} from 'react-navigation-tabs'
import {ScreenOne, ScreenTwo, ScreenThree} from './Screens'
```

Now we create the respective screens for each navigator item. Create a new file and name it **Screens.js** and input the following

```
import React from 'react'
import {View, Text, StyleSheet} from 'react-native'
const ScreenOne = () => {
    return(
        <View style={styles.container}>
        <Text>This is the first screeen</Text>
        </View>
    )
}

const ScreenTwo = () =>{
    return(
         <View style={styles.container}>
        <Text>This is the second screeen</Text>
        </View>
    )
}

const ScreenThree = () =>{
    return(
         <View style={styles.container}>
        <Text>This is the third screeen</Text>
        </View>
    )
}

const styles = StyleSheet.create({
    container:{
        flex: 1,
        backgroundColor: '#fff',
        justifyContent: 'center',
        alignItems: 'center'
    }
})
export {ScreenOne, ScreenTwo, ScreenThree}
```

Back to our App.js, let's map our respective screens to the bottom tab component. It receives two arguement. An object containing the screens, and the configuration for the bottom tab component We continue the code:
```
const bottomComponent = createBottomTabNavigator({
    Home:{
        screen: ScreenOne,
        navigationOptions:{
        tabBarIcon: ({tintColor}) => <Ionicons name='md-home' color={tintColor} size={24} />
    }
    },
    Notifications:{
        screen: ScreenTwo,
        navigationOptions:{
        tabBarIcon: ({tintColor}) => <Ionicons name='md-notifications' color={tintColor} size={24} />
    }
    },
    Profile:{
        screen: ScreenThree,
        navigationOptions:{
        tabBarIcon: ({tintColor}) => <Ionicons name='md-person' color={tintColor} size={24} />
    }
    }
})
```

We can customize the look of the bottom bar with the **BottomTabBar** component imported from **createBottomTabNavigator**. To do this, we add our we create a configuration object that would be passed to the **createBottomTabNavigator**. It should look like this:

```
const config= {
    tabBarOptions:{
    activeTintColor: '#fff',
    inactiveTintColor: 'rgba(0,0,0,0.7)'
  },
  tabBarComponent: (props) => <CustomBottomBar {...props} />
}
```
Our createBottomTabNavigator should now look like this:
```
const bottomNavigator = createBottomTabNavigator({
  Home:{
    screen: ScreenOne,
    navigationOptions:{
      tabBarIcon: ({tintColor}) => <Ionicons name='md-home' color={tintColor} size={24} />
    }
  },
  Notifications:{
    screen: ScreenTwo,
    navigationOptions:{
      tabBarIcon: ({tintColor}) => <Ionicons name='md-notifications' color={tintColor} size={24} />
    }
  },
  Profile:{
    screen: ScreenThree,
    navigationOptions:{
      tabBarIcon: ({tintColor}) => <Ionicons name='md-person' color={tintColor} size={24} />
    }
  }
}, config)
```

The important property here is the **tabBarComponent**. This accepts a component to act as the default bottom tab bar. We named this **CustomBottomBar**. It also has props that should be passed down to it so that the custom view object still behaves like the default tab bar. React navigation gives us access to the BottomTabBar component so we can configure such component into how we want this to look instead of using the default look and feel of the bottom component.

Now, let's go on to creating the **CustomBottomBar** component. Still in the same **App.js**, do the following:

```
const CustomBottomBar = (props) =>{
    //We use the spread operator to pass down all default properties of a bottom bar
    return(
        <View>
        <BottomBar {...props} style={{backgroundColor: 'tranparent'}}>
        </View>
    )
}
```
Note that the background color of the *BottomBar* should be set to *transparent* so that it doesn't overshadow the custom indicator. 
To avoid complexity, we won't be adding any further styling to the bottom bar. You'll notice we wrapped a view around the **BottomBar** component. This is so that we can add a custom indicator of our choice to it. Since our aim is to create that movable colored view in the image above, we add a new component to the **CustomBottomBar**. The code should now look like this:

```
const CustomBottomBar = (props) =>{
    //We use the spread operator to pass down all default properties of a bottom bar

    //custom styles for our indicator
    //The width of the indicator should be of equal size with each tab button. We have 3 tab buttons therefore, the width of a single tab button would be the total width Dimension of the screen divided by 3

    const {width} = Dimensions.get('screen')
    const animStyles = {
        position: 'absolute',
        top: 0,
        left: 0,
        bottom:0,
        width: width/3,
        backgroundColor: 'rebeccapurple'
    }
    return(
        <View>
        <Animated.View style={animStyles} />
        <BottomTabBar {...props} style={{backgroundColor: 'tranparent'}} />
        </View>
    )
}
```

This is how the app should look.
![Screesnhot](https://dev-to-uploads.s3.amazonaws.com/i/m165t5czxhphl9hhvyha.png)



Now, we animate it. The indicator is required to move to the very position of the tab button pressed. The **BottomTab** component receives an *onTabPress* prop which is a method and this returns the route name of the tab pressed. This method executes an action everytime a tab button is pressed. We'll use this action to properly animate the indicator to the specific location. Still in the **CustomBottomBar** component, we modify it as:

```
const CustomBottomBar = (props) =>{
    //We use the spread operator to pass down all default properties of a bottom bar

    //custom styles for our indicator
    //The width of the indicator should be of equal size with each tab button. We have 3 tab buttons therefore, the width of a single tab button would be the total width Dimension of the screen divided by 3

    const {width} = Dimensions.get('screen')

    //Create an animated value 
    const [position] = useState(new Animated.ValueXY())

    //We attach the x,y coordinates of the position to the transform property of the indicator so we can freely animate it to any position of our choice.
    const animStyles = {
        position: 'absolute',
        top: 0,
        left: 0,
        bottom:0,
        width: width/3,
        backgroundColor: 'rebeccapurple',
        transform: position.getTranslateTransform()
    }
    return(
        <View>
        <Animated.View style={animStyles} />
        <BottomTabBar {...props} style={{backgroundColor: 'tranparent'}} />
        </View>
    )
}
```

With everything set, we can now animate our position to where we want it. We create a method called *animate*, This method would be passed to the *onTabPress* prop of the **BottomBar** component. So we should have our code like this:

```
const CustomBottomBar = (props) =>{
    //We use the spread operator to pass down all default properties of a bottom bar

    //custom styles for our indicator
    //The width of the indicator should be of equal size with each tab button. We have 3 tab buttons therefore, the width of a single tab button would be the total width Dimension of the screen divided by 3

    const {width} = Dimensions.get('screen')

    //Create an animated value 
    const [position] = useState(new Animated.ValueXY())

    //We attach the x,y coordinates of the position to the transform property of the indicator so we can freely animate it to any position of our choice.
    const animStyles = {
        position: 'absolute',
        top: 0,
        left: 0,
        bottom:0,
        width: width/3,
        backgroundColor: 'rebeccapurple',
        transform: position.getTranslateTransform()
    }

    const animate = (value, route) =>{
        //navigate to the selected route on click
        props.navigation.navigate(route)

        //animate indicator
        Animated.timing(position, {
            toValue: {x: value, y: 0},
            duration: 300,
            useNativeDriver: true
        }).start()
    }

    return(
        <View>
        <Animated.View style={animStyles} />
        <BottomTabBar {...props} onTabPress={({route}) =>{
            switch(route.key){
                case 'Home':
                //animated position should be 0
                     animate(0, route.key)
                     break
                     case 'Notifications':
                     //animated position is width/3
                      animate(width/3 , route.key)
                      break
                      case 'Profile':
                      //animated position is width of screen minus width of single tab button
                       animate(width - (width/3), route.key)
                       break
            }
        }} style={{backgroundColor: 'transparent'}} />
        </View>
    )
}
```

And that's it. We have successfully animated our indicator for the bottom bar to focus on the actively selected screen of the bottom bar. Here is the full. We just had to modify the **BottomTabBar** component imported from *react-navigation-tabs* as we would do for a normal react component. Nothing much here!