Shared element transitions in React Native involve smoothly transitioning the position, size, and appearance of elements between two screens. This is often used for a seamless user experience when navigating between screens.
Prerequisite:
Install and configure by following below links:
- https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/#installation
- https://reactnavigation.org/docs/getting-started/#wrapping-your-app-in-navigationcontainer
- https://reactnavigation.org/docs/hello-react-navigation#installing-the-native-stack-navigator-library
- https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/#installation
After doing above step lets move to coding. I have added inline comments for understating.
RenderItem.js (flatlist render item)
import {Pressable, StyleSheet, Text, View} from 'react-native';
import React from 'react';
import {useNavigation} from '@react-navigation/native';
import Animated, {FadeInDown} from 'react-native-reanimated';
const RenderItem = ({item, index}) => {
const navigation = useNavigation();
return (
// Animated.View for fading in the item with delay based on index
<Animated.View entering={FadeInDown.delay(200 * index)}>
<Pressable
style={styles.container}
onPress={() => {
navigation.navigate('Details', {item: item});
}}>
{/* Animated.Image with sharedTransitionTag is used to create a visually appealing shared element transition between screens when navigating, ensuring a smooth animation of the image as it moves from one screen to another. */}
<Animated.Image
source={item.image}
style={styles.image}
sharedTransitionTag={item.name}
/>
<View style={styles.textContainer}>
<Text style={styles.textName}>{item.name}</Text>
<Text style={styles.textLocation}>{item.location}</Text>
</View>
</Pressable>
</Animated.View>
);
};
export default RenderItem;
Detail Screen
import {StyleSheet, Text, View, useWindowDimensions} from 'react-native';
import React from 'react';
import Animated, {FadeIn, FadeInDown} from 'react-native-reanimated';
import Header from '../components/Header';
import Button from '../components/Button';
import LinearGradient from 'react-native-linear-gradient';
const Detail = ({route}) => {
const {item} = route.params;
const {width} = useWindowDimensions();
return (
<LinearGradient colors={['#a8ff78', '#78ffd6']} style={styles.container}>
<Header />
<View>
<View>
<Animated.Image
sharedTransitionTag={item.name}
source={item.image}
style={{width: width, height: width}}
/>
<Animated.View
style={styles.textContainer}
entering={FadeIn.delay(600)}>
<Text style={styles.textName}>{item.name}</Text>
<Text style={styles.textLocation}>{item.location}</Text>
</Animated.View>
</View>
<Animated.View entering={FadeInDown.delay(800)}>
<Text style={styles.textAbout}>About</Text>
<Text style={styles.text}>{item.about}</Text>
</Animated.View>
</View>
<Button />
</LinearGradient>
);
};
export default Detail;
Header of Detail Screen
/* eslint-disable react-native/no-inline-styles */
import {Image, Platform, Pressable, StyleSheet} from 'react-native';
import React from 'react';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import Animated, {FadeIn} from 'react-native-reanimated';
import {useNavigation} from '@react-navigation/native';
const Header = () => {
const inset = useSafeAreaInsets();
const navigation = useNavigation();
return (
<Animated.View
style={[styles.container, {top: Platform.OS === 'ios' ? inset.top : 20}]}
entering={FadeIn.delay(400)}>
<Pressable
onPress={() => {
navigation.goBack();
}}>
<Image
source={require('../assets/chevron.png')}
style={styles.chevron}
/>
</Pressable>
<Pressable
onPress={() => {
console.log('LIKE');
}}>
<Image source={require('../assets/like.png')} style={styles.chevron} />
</Pressable>
</Animated.View>
);
};
export default Header;
Bottom Button on detail screen
import {Pressable, StyleSheet, Text, useWindowDimensions} from 'react-native';
import React from 'react';
import Animated, {FadeInDown} from 'react-native-reanimated';
const Button = () => {
const {width} = useWindowDimensions();
// Creating an Animated Pressable component using createAnimatedComponent //ALTERNATE:==> <Animated.View/>
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
return (
<AnimatedPressable
style={[styles.container, {width: width * 0.9}]} // Styles for the Pressable
entering={FadeInDown.delay(1000)} // FadeInDown animation with a delay of 1000 milliseconds
onPress={() => {
console.log('BOOKING NOW');
}}>
<Text style={styles.text}>Booking Now</Text>
</AnimatedPressable>
);
};
export default Button;