Creación de un Carousel en React Native sin Librerías Externas
Los carousels son elementos comunes en las aplicaciones móviles que permiten a los usuarios
desplazarse de forma horizontal entre diferentes elementos, como imágenes o tarjetas de producto.
Aunque existen muchas librerías disponibles para crear carousels en React Native,
en este tutorial aprenderemos a crear uno desde cero sin depender de ninguna librería externa.
Creamos un componente Carousel en Carousel.tsx
javascriptimport React, { useState, useRef } from 'react'; import { Dimensions, FlatList, TouchableOpacity, View } from 'react-native'; import { MaterialIcons } from '@expo/vector-icons'; export type ISliderItem = { [key: string]: any; } type RenderItemProps = { item: ISliderItem; onPress: (item: ISliderItem) => void; }; type SliderProps = { items: ISliderItem[] onPress: (item: ISliderItem) => void; renderItem: ({ item, onPress }: RenderItemProps) => JSX.Element; dots?: boolean; arrows?: boolean; dotsFloating?: boolean; } const { width: windowWidth } = Dimensions.get('window'); const Carousel = (props) => { // EVENTOS, ESTADOS Y FUNCIONES // ... // RENDER return(...) } export default Carousel
Definimos las funciones, eventos y los estados que estamos vamos usar para este ejemplo
javascriptconst { items = [], onPress, dots = true, arrows = true, dotsFloating = false } = props; const [currentPage, setCurrentPage] = useState(0); const flatListRef = useRef(null); const _spacing = 0; // Funciones para cambiar de página const handlePageChange = (newPage) => { setCurrentPage(newPage); flatListRef.current?.scrollToIndex({ index: newPage, animated: true }); }; const goToNextPage = () => { if (currentPage < items.length - 1) { const nextPage = currentPage + 1; setCurrentPage(nextPage); flatListRef.current?.scrollToIndex({ index: nextPage, animated: true }); } }; const goToPreviousPage = () => { if (currentPage > 0) { const previousPage = currentPage - 1; setCurrentPage(previousPage); flatListRef.current?.scrollToIndex({ index: previousPage, animated: true }); } };
para este ejemplo vamos usar el FlatList
de React Native para mostrar los elementos del carousel de manera horizontal.
javascript// prevButton arrow <FlatList ref={flatListRef} style={{ flexGrow: 0 }} data={items} keyExtractor={(item) => item.id} horizontal pagingEnabled renderItem={({ item }) => renderItem({ item, onPress })} showsHorizontalScrollIndicator={false} contentContainerStyle={{ paddingLeft: _spacing }} onMomentumScrollEnd={(event) => { const newPage = Math.round(event.nativeEvent.contentOffset.x / windowWidth); setCurrentPage(newPage); }} /> // NextButton arrow // DOTS
Ahora definiremos las secciones de navegación arrows
y los dots
del carousel.
javascript// prevButton {arrows ? ( <TouchableOpacity onPress={goToPreviousPage} style={[styles.navigationButton, styles.leftButton]} > <MaterialIcons name="arrow-back-ios" size={16} color="black" /> </TouchableOpacity> ) : null} // NextButton {arrows ? ( <TouchableOpacity onPress={goToNextPage} style={[styles.navigationButton, styles.rightButton]} > <MaterialIcons name="arrow-forward-ios" size={16} color="black" /> </TouchableOpacity> ) : null} // Dots {dots ? ( <View style={[styles.dotContainer, dotsFloating && styles.dotContainerFloating]}> {items.map((_, index) => ( <TouchableOpacity key={index} style={[ styles.dot, index === currentPage && styles.activeDot ]} onPress={() => handlePageChange(index)} /> ))} </View> ) : null}
Ahora agreamos los estilos base para nuestro carousel.
Crear un archivo styles.ts
javascriptimport { StyleSheet } from "react-native"; import { theme } from "@components/theme"; const { colors } = theme; export const styles = StyleSheet.create({ contentContainerStyle: { width: "100%", }, dotContainer: { flexDirection: "row", justifyContent: "center", alignItems: "center", marginTop: 10, }, dot: { width: 10, height: 10, borderRadius: 5, backgroundColor: "#E1E1E1", marginHorizontal: 5, }, navigationButton: { position: "absolute", top: "40%", paddingHorizontal: 5, paddingVertical: 5, borderRadius: 10, zIndex: 2, }, leftButton: { left: 10, flex: 1, }, rightButton: { right: 10, }, activeDot: { backgroundColor: colors.primary.red, width: 32, }, dotContainerFloating: { bottom: 30, }, });
Ejemplo de uso
para mi prueba tengo un carousel de imagenes, para esto tengo lo siguiente
javascriptexport default function Demo() { const images = [ { id: "image-1", "_type": "image", "alt": "", "link": "", "title": ", " }, ... ] function ProductImage({ image }: ProductImageProps) { const { link, alt } = image; return ( <View style={{ ...styles.container, width: windowWidth }}> <Image source={{ uri: link }} style={styles.image} alt={alt} /> </View> ) } return( <View style={styles.container} > <Carousel items={images} dotsFloating={false} arrows={false} onPress={(item) => { console.log('item: ', item); }} renderItem={({ item }: any) => ( <ProductImage image={item} onPress={() => { console.log('item: ', item); }} /> )} /> </View> ) } const styles = StyleSheet.create({ container: { width: "90%", backgroundColor: "#fff", }, image: { width: 244, height: 260, resizeMode: "stretch", alignSelf: "center", }, });
Deja tu comentario!
Su dirección de correo electrónico no será publicada.