React Native Custom Animated Sliding Drawer Android iOS Tutorial

In this tutorial, I am going to explain you how we can create own custom animated sliding drawer in react native from scratch with complete source code.

Note: In this tutorial, I have used a nice hamburger icon. You can download this nice hamburger icon and create a folder named assets under your react native project directory to access this icon in your app.

Let’s get started…

Step – 1: Create a new and fresh react native project or create a new custom module in your existing react native project. If you don’t know then follow this tutorial or If you want to create your own custom module and don’t know how to create a custom module in an existing react native project then follow this tutorial.

I am going to explain through creating a new react native project.

Step – 2: Open your newly created project or App.js  file into your any favourite code editor.

Note: Facebook has removed the concept of different files( index.android.js / index.ios.js  ) for different platforms in latest version( 0.49  ) of react native and allows developer to write code in App.js  file.

Step – 3: Import all required components from react , react-native  packages.

import React, { Component } from 'react';
import { View, Text, Platform, TouchableOpacity, Animated, StyleSheet, Image } from 'react-native';

Step – 4: Define default width for our custom animated sliding drawer.

const DRAWER_WIDTH = 300;
Note: In this tutorial, I have used fixed width for our custom animated sliding drawer. You can change this default width according to your requirement or you can set width for sliding drawer based on device width. If you don’t know how you can get device width and height then follow this tutorial.

Step – 5: Write code for App  component. Latest version of react native provides App  component by default. So, I am going to use this pre-created component rather than to create new one. You can create your own custom component according to your requirement.

export default class App extends Component<{}>
{
    constructor()
    {
        super();

        this.animatedValue = new Animated.Value(0);

        this.state = { disabled: false }

        this.toggleFlag = 0;
    }

    toggleDrawer = () =>
    {
        // Code to show / hide custom animated sliding drawer
    }

    render()
    {
        // Set design
    }
}

Explanation:

  • this.animatedValue  is used for animation propose.
  • disabled  state variable is used to disable the hamburger icon button during animation.
  • this.toggleFlag  variable is used to apply the toggle functionality on hamburger icon button.

Step – 6: Implement toggleDrawer  function of App  component.

toggleDrawer = () =>
{
    if( this.toggleFlag == 0 )
    {
        this.setState({ disabled: true }, () =>
        {
            Animated.timing(
                this.animatedValue,
                {
                    toValue: 1,
                    duration: 250
                }
            ).start(() =>
            {
                this.setState({ disabled: false });
                this.toggleFlag = 1;
            });
        });            
    }
    else
    {
        this.setState({ disabled: true }, () =>
        {
            Animated.timing(
                this.animatedValue,
                {
                    toValue: 0,
                    duration: 250
                }
            ).start(() =>
            {
                this.setState({ disabled: false });
                this.toggleFlag = 0;
            });
        });  
    }
}

Explanation:

  • If toggleFlag  variable’s value is 0 then it means our custom animated sliding drawer is hidden and If toggleFlag  variable’s value is 1 then it means our custom animated sliding drawer is shown. So if toggleFlag  variable’s value is 0 then drawer is hidden and now first of all we have to disable the hamburger icon button to prevent the execution of same animation code if animation is already in progress and then show our custom animated sliding drawer with nice and smooth animation and after completing the animation we will enable the hamburger icon button.
  • Animated.timing  function is used to change the this.animated  value from 0 to 1 ( in case if drawer is hidden and want to show ) with duration of 250(ms) and again change the this.animated  value from 1 to 0 ( in case if drawer is shown and want to hidden ) with same duration of 250(ms) and change disabled state and toggleFlag variable’s values after completing each ( hidden / shown ) animation.

Step – 7: Implement render  function of App  component.

render()
{
    const animatedValue = this.animatedValue.interpolate(
    {
        inputRange: [ 0, 1 ],
        outputRange: [ DRAWER_WIDTH - 46, 0 ]
    });

    return(
        <View style = { styles.container }>
            <Text style = { styles.demoText }>Animated Sliding Drawer Tutorial.</Text>
            <Animated.View style = {[ styles.drawer, { transform: [{ translateX: animatedValue }]}]}>
                <TouchableOpacity disabled = { this.state.disabled } onPress = { this.toggleDrawer } style = {{ padding: 8 }}>
                    <Image source = { require('./assets/hamburger.png') } style = {{ width: 30, height: 30, resizeMode: 'contain' }} />
                </TouchableOpacity>
                <View style = { styles.drawerContainer }>                        
                    <Text style = { styles.text }>Your Content goes here...</Text>  
                </View>                
            </Animated.View>
        </View>
    );
}

Explanation:

  • interpolate  function maps the input ranges to output ranges and interpolates the values before updating the property.
  • Have you noticed that I am subtracting the number 46 from DRAWER_WIDTH constant as a first parameter of outputRange because If you noticed that hamburger icon image’s width is 30px and applied padding of 8px to hamburger icon button. So, If we don’t subtract 46 from DRAWER_WIDTH then hamburger icon button will not be shown and completly goes out of the screen.
Note: The reason behind subtracting 46 from DRAWER_WIDTH is our hamburger image icon’ width is 30px and padding of 8px has been applied to the button. So, ( width of hamburger image icon ( 30 ) + button’s left padding ( 8 ) + button’s right paddding ( 8 ) ) = 46 .

Step – 8: Implement styles for required components.

const styles = StyleSheet.create(
{
    container:
    {
        flex: 1,
        backgroundColor: '#ddd',
        justifyContent: 'center',
        alignItems: 'center'
    },

    demoText:
    {
        fontSize: 20,
        textAlign: 'center',
        color: 'black'
    },

    drawer:
    {
        position: 'absolute',
        top: (Platform.OS == 'ios') ? 20 : 0,
        right: 0,
        bottom: 0,
        width: DRAWER_WIDTH,
        flexDirection: 'row'
    },

    drawerContainer:
    {
        flex: 1,
        backgroundColor: 'rgb(57, 73, 171)',
        paddingHorizontal: 10,
        justifyContent: 'center',
        alignItems: 'center'
    },

    text:
    {
        fontSize: 25,
        color: 'white',
        textAlign: 'center'
    }
});

Complete Source Code for App.js file:

import React, { Component } from 'react';
import { View, Text, Platform, TouchableOpacity, Animated, StyleSheet, Image } from 'react-native';

const DRAWER_WIDTH = 300;

export default class App extends Component<{}>
{
    constructor()
    {
        super();

        this.animatedValue = new Animated.Value(0);

        this.state = { disabled: false }

        this.toggleFlag = 0;
    }

    toggleDrawer = () =>
    {
        if( this.toggleFlag == 0 )
        {
            this.setState({ disabled: true }, () =>
            {
                Animated.timing(
                    this.animatedValue,
                    {
                        toValue: 1,
                        duration: 250
                    }
                ).start(() =>
                {
                    this.setState({ disabled: false });
                    this.toggleFlag = 1;
                });
            });            
        }
        else
        {
            this.setState({ disabled: true }, () =>
            {
                Animated.timing(
                    this.animatedValue,
                    {
                        toValue: 0,
                        duration: 250
                    }
                ).start(() =>
                {
                    this.setState({ disabled: false });
                    this.toggleFlag = 0;
                });
            });  
        }
    }

    render()
    {
        const animatedValue = this.animatedValue.interpolate(
        {
            inputRange: [ 0, 1 ],
            outputRange: [ DRAWER_WIDTH - 46, 0 ]
        });

        return(
            <View style = { styles.container }>
                <Text style = { styles.demoText }>Animated Sliding Drawer Tutorial.</Text>
                <Animated.View style = {[ styles.drawer, { transform: [{ translateX: animatedValue }]}]}>
                    <TouchableOpacity disabled = { this.state.disabled } onPress = { this.toggleDrawer } style = {{ padding: 8 }}>
                        <Image source = { require('./assets/hamburger.png') } style = {{ width: 30, height: 30, resizeMode: 'contain' }} />
                    </TouchableOpacity>
                    <View style = { styles.drawerContainer }>                        
                        <Text style = { styles.text }>Your Content goes here...</Text>  
                    </View>                
                </Animated.View>
            </View>
        );
    }
}

const styles = StyleSheet.create(
{
    container:
    {
        flex: 1,
        backgroundColor: '#ddd',
        justifyContent: 'center',
        alignItems: 'center'
    },

    demoText:
    {
        fontSize: 20,
        textAlign: 'center',
        color: 'black'
    },

    drawer:
    {
        position: 'absolute',
        top: (Platform.OS == 'ios') ? 20 : 0,
        right: 0,
        bottom: 0,
        width: DRAWER_WIDTH,
        flexDirection: 'row'
    },

    drawerContainer:
    {
        flex: 1,
        backgroundColor: 'rgb(57, 73, 171)',
        paddingHorizontal: 10,
        justifyContent: 'center',
        alignItems: 'center'
    },

    text:
    {
        fontSize: 25,
        color: 'white',
        textAlign: 'center'
    }
});

Step – 9: Apply react-native run-android  command to run app in android devices or apply react-native run-ios  command to run app in ios devices.

  • iOS Screenshot:

  • Android Screenshot:

Enjoy Guys… 😀

Leave a Reply

Your email address will not be published. Required fields are marked *