Animation has become an integral part of mobile development, thanks to React native for its Animated API. In this, we will be building a custom modal with the help of react native animated API using just a button click.
A Modal component is a basic way to present content above an enclosing view.
Prerequisite
Node >= 16.0.0
Expo CLI
To get started with the custom modal we need to generate a new expo project with the following command line.
npx create-expo-app -t expo-template-blank-typescript
cd into the project and run:
yarn install @expo/vector-icons
yarn start or npm start
Screen Layout
In the project, directory create a screen folder and a tsx
file with the name src/Screens/ModalScreen.tsx
import React, { useState } from "react";
import { View, StyleSheet, Image, Text, TouchableOpacity } from "react-native";
const ModalScreen = () => {
return (
<View>
<Text> Modal Screen</Text>
</View>
);
};
export default ModalScreen;
We also need to create the modal component, inside the project directory create the component folder Components/Modals/ModalLayout.tsx
import { View, Text, StyleSheet, Modal, Animated } from "react-native";
import React from "react";
const ModalLayout = () => {
return (
<View>
<Text>Modal Layout</Text>
</View>
);
};
export default ModalLayout;
At the ModalLayout.tsx
we need to import react hooks useEffect, useState, useRef
to manage our states and also the Animated
API together with Modal
component from react-native.
The Animated
library is designed to make animations fluid, powerful, and painless to build and maintain. Animated
focuses on declarative relationships between inputs and outputs, configurable transforms in between, and start
/stop
methods to control time-based animation execution.
To calculate the scale value of our animation, we will use Animated
value type Animated.Value()
for single values.
const scaleValue = useRef(new Animated.Value(0)).current;
A function toggleModal is created to help with the animation mechanism.
const scaleValue = useRef(new Animated.Value(0)).current;
const [showModal, setShowModal] = useState(visible);
useEffect(() => {
toggleModal();
}, [visible]);
const toggleModal = () => {
if (visible) {
setShowModal(true);
Animated.spring(scaleValue, {
toValue: 1,
useNativeDriver: true,
}).start();
} else {
setTimeout(() => setShowModal(false), 200);
Animated.timing(scaleValue, {
toValue: 0,
duration: 300,
useNativeDriver: true,
}).start();
}
};
The full ModalLayout.tsx
will look like this.
import { View, StyleSheet, Modal, Animated } from "react-native";
import React, { useState, useRef, useEffect } from "react";
import { COLORS } from "../../utils";
type Props = {
visible: boolean;
children: ReactNode;
};
const ModalLayout = ({ children, visible }: Props) => {
const scaleValue = useRef(new Animated.Value(0)).current;
const [showModal, setShowModal] = useState(visible);
useEffect(() => {
toggleModal();
}, [visible]);
const toggleModal = () => {
if (visible) {
setShowModal(true);
Animated.spring(scaleValue, {
toValue: 1,
useNativeDriver: true,
}).start();
} else {
setTimeout(() => setShowModal(false), 200);
Animated.timing(scaleValue, {
toValue: 0,
duration: 300,
useNativeDriver: true,
}).start();
}
};
return (
<Modal transparent visible={showModal}>
<View style={styles.container}>
<Animated.View
style={[styles.modalCon, { transform: [{ scale: scaleValue }] }]}
>
{children}
</Animated.View>
</View>
</Modal>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: COLORS.modalbackgroundcolor,
},
modalCon: {
backgroundColor: "white",
paddingHorizontal: 20,
paddingVertical: 30,
borderRadius: 20,
elevation: 20,
width: "80%",
},
});
export default ModalLayout;
Button Component
create Button.tsx
inside the project directory src/Components/Buttons/Button.tsx
import { Text, TouchableOpacity, StyleSheet } from "react-native";
import React from "react";
import { COLORS } from "../../utils";
type ButtonProps = {
onPress: () => void;
title: string;
};
const Button = ({ onPress, title }: ButtonProps) => {
return (
<TouchableOpacity
onPress={onPress}
activeOpacity={0.9}
style={styles.button}
>
<Text style={styles.title}>{title}</Text>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
button: {
height: 55,
width: "65%",
backgroundColor: COLORS.red,
marginVertical: 10,
justifyContent: "center",
alignItems: "center",
borderRadius: 5,
},
title: {
color: COLORS.white,
fontWeight: "bold",
fontSize: 18,
},
});
export default Button;
Modal Screen
In the modal screen import ModalLayout.tsx
and Button.tsx
components inside ModalScreen.tsx
go ahead and import Entypo
from @expo/vector-icons
import React, { useState } from "react";
import { View, StyleSheet, Image, Text, TouchableOpacity } from "react-native";
import { Button } from "../Components/Buttons";
import { Entypo } from "@expo/vector-icons";
import ModalLayout from "../Components/Modals/ModalLayout";
const ModalScreen = () => {
const [visible, setVisible] = useState(false);
return (
<View style={styles.container}>
<ModalLayout visible={visible}>
<View style={{ alignItems: "center" }}>
<View style={styles.header}>
<TouchableOpacity onPress={() => setVisible(false)}>
<Entypo
name="cross"
style={{ height: 30, width: 30 }}
size={24}
color="black"
/>
</TouchableOpacity>
</View>
</View>
<View style={{ alignItems: "center" }}>
<Image
source={require(".././assets/imageIcons/success.png")}
style={{ height: 150, width: 150, marginVertical: 10 }}
/>
</View>
<Text style={{ marginVertical: 30, fontSize: 20, textAlign: "center" }}>
Transaction Successful
</Text>
</ModalLayout>
<Button title="Show Modal" onPress={() => setVisible(true)} />
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
header: {
width: "100%",
height: 40,
alignItems: "flex-end",
justifyContent: "center",
},
});
export default ModalScreen;
create src/utils/Colors.tsx
in the project directory
const COLORS = {
modalbackgroundcolor: "rgba(0,0,0,0.5)",
modalbackgroundContainerColor: "white",
white: "#FFFFFF",
red: "#FF0000",
};
export default COLORS;
The custom modal should look like this
GitHub link to the tutorial: Github Link
Conclusion
The tutorial completes how we can use Animated API and Modal component from react-native to build our custom modal in our mobile projects. kindly share this article and follow on Twitter
Top comments (0)