In this tutorial, weโll create a beautiful and optimized image gallery in React Native with a fullscreen preview and a thumbnail navigation system. ๐
โจ Features
- Smooth scrolling with horizontal pagination ๐
- Thumbnail navigation to jump to a specific image ๐
- Optimized performance using useCallback and useRef โก
- Dynamic styling for the active thumbnail ๐จ
Let's dive into the implementation! ๐โโ๏ธ
๐๏ธ Setting Up the Component
First, import the necessary modules and initialize the component. We'll use React Native's FlatList, Image, TouchableOpacity, and useWindowDimensions for a responsive design.
import {
FlatList,
Image,
StyleSheet,
TouchableOpacity,
useWindowDimensions,
View,
} from 'react-native';
import React, { useCallback, useRef, useState } from 'react';
const [activeIndex, setActiveIndex] = useState(0);
const topRef = useRef(null);
const thumbRef = useRef(null);
const {width, height} = useWindowDimensions();
const IMAGE_SIZE = 80;
const SPACING = 10;
๐ Core Implementation
๐ฅ Fullscreen Image Viewer
The FlatList will render the images in full screen, allowing users to swipe through them smoothly.
const fullScreenRenderItem = useCallback(
({ item }) => (
<View style={{ width, height }}>
<Image source={{ uri: item }} style={styles.fullScreenImage} />
</View>
),
[]
);
๐ผ๏ธ Thumbnail Navigation
We'll create another FlatList below the fullscreen view to display thumbnails. Users can tap a thumbnail to navigate to the corresponding full-sized image.
const thumbnailContentRenderItem = useCallback(
({ item, index }) => {
const isActive = activeIndex === index;
return (
<TouchableOpacity onPress={() => scrollToActiveIndex(index)}>
<Image
source={{ uri: item }}
style={[styles.thumbnailImage(IMAGE_SIZE, SPACING, isActive)]}
/>
</TouchableOpacity>
);
},
[activeIndex]
);
๐ Syncing Thumbnails with Fullscreen View
To ensure smooth scrolling between thumbnails and full images, we handle the onMomentumScrollEnd event and calculate the active index dynamically.
const onMomentumScrollEnd = useCallback(
event => {
const newIndex = Math.round(event.nativeEvent.contentOffset.x / width);
scrollToActiveIndex(newIndex);
},
[width]
);
The scrollToActiveIndex function keeps both lists in sync:
const scrollToActiveIndex = index => {
setActiveIndex(index);
topRef.current?.scrollToOffset?.({ offset: index * width, animated: true });
const thumbnailOffset = index * (IMAGE_SIZE + SPACING) - width / 2 + IMAGE_SIZE / 2;
thumbRef.current?.scrollToOffset?.({ offset: Math.max(thumbnailOffset, 0), animated: true });
};
๐๏ธ Rendering the Component
Finally, we integrate both FlatList components into our main view:
return (
<View style={styles.container}>
<FlatList
ref={topRef}
data={metaData}
renderItem={fullScreenRenderItem}
keyExtractor={keyExtractor}
horizontal
showsHorizontalScrollIndicator={false}
pagingEnabled
onMomentumScrollEnd={onMomentumScrollEnd}
/>
<FlatList
ref={thumbRef}
data={metaData}
renderItem={thumbnailContentRenderItem}
keyExtractor={keyExtractor}
horizontal
contentContainerStyle={styles.thumbnailContainer}
showsHorizontalScrollIndicator={false}
style={styles.thumbnailList(IMAGE_SIZE)}
/>
</View>
);
๐จ Styling
To make everything look sleek, we define the styles:
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000',
},
fullScreenImage: {
...StyleSheet.absoluteFillObject,
resizeMode: 'cover',
},
thumbnailImage: (IMAGE_SIZE, SPACING, isActive) => ({
width: IMAGE_SIZE,
height: IMAGE_SIZE,
borderRadius: 12,
marginRight: SPACING,
borderWidth: 2,
resizeMode: 'cover',
borderColor: isActive ? '#fff' : 'transparent',
}),
thumbnailContainer: {
paddingHorizontal: 10,
},
thumbnailList: IMAGE_SIZE => ({
position: 'absolute',
bottom: IMAGE_SIZE,
}),
});
๐ฏ Conclusion
With this implementation, we have created an interactive and optimized image gallery ๐ท with smooth transitions and thumbnail navigation. ๐
This gallery is highly performant thanks to useCallback, useRef, and proper scroll synchronization. โก Try customizing it further by adding animations or gestures for an even better user experience! ๐
Happy coding! ๐ป๐ฅ
Top comments (0)