Search code examples
react-nativereact-native-flatlistreact-native-scrollviewreact-native-stylesheet

React-Native FlatList with 3 cards paging layout


In this snack I am trying to have 3 cards in the center of the screen with a horizontal FlatList and enabled paging to jump to the next 3 cards on scroll.

But the layout starts getting destroyed after scrolling and some pixels of the next/previous card appears in the view.

How should I style this list to always have exactly 3 cards in the center of the screen and scroll will jump to the next page with the next 3 cards ? Or like the GooglePlay store, a fixed pixels of previous/next card be visible to the left and right of the main 3 cards. (Example screenshots below)

 <View style={{flex:1,justifyContent: 'center', marginLeft: 5, marginRight: 5,}}>
      <FlatList
        horizontal
        pagingEnabled
        data={data}
        keyExtractor={(item) => `ìtem-${item}`}
        renderItem={({ item }) => (
          <Card style={{width:Dimensions.get("window").width/3-5,marginRight:5}}>
            {/* some content */}
          </Card>
        )}
      />
 </View>

I do not need a library like snap-carousel or so ...

enter image description here


Solution

  • use Scrollview prop snapToOffsets to achieve that.

    like google play example ( one by one ) try snack.

    enter image description here

    your example ( three by three ) try snack.

    enter image description here

    how to use snapToOffsets?

    const snapToOffsetsLikeGooglePlay = data.map((x, i) => {
        return ((i * itemWidth) + startScroll)
    })
    
    const snapToOffsetsLikeYourExample = data.map((x, i) => {
        return ((i * (itemWidth) * previewCount) + startScroll)
    })
    
    //see the example below to know 
    //what is `startScroll` and `previewCount` mean? 
    //and how to calculate `itemWidth`?
    

    here the full example

    import React from 'react';
    import {FlatList, Text} from 'react-native';
    import { View, StyleSheet, ScrollView, Dimensions } from 'react-native';
    
    const { width } = Dimensions.get('window');
    //you need to preview n items.
    const previewCount = 3;
    //to center items
    //the screen will show `previewCount` + 1/4 firstItemWidth + 1/4 lastItemWidth 
    //so for example if previewCount = 3
    //itemWidth will be =>>> itemWidth = screenWidth / (3 + 1/4 + 1/4)
    const itemWidth = width/(previewCount + .5);
    //to center items you start from 3/4 firstItemWidth 
    const startScroll = (itemWidth * 3/4);
    
    
    const App = () => {
    
        const data = [...Array(24).keys()];
        const flatlistRef = React.useRef();
    
        
        React.useEffect(() => {
            if (flatlistRef.current) flatlistRef.current.scrollToOffset({
                offset:startScroll, animated: false
            });
        }, [flatlistRef]);
    
    
        const snapToOffsetsLikeGooglePlay = data.map((x, i) => {
            return ((i * itemWidth) + startScroll)
        })
    
        const snapToOffsets = data.map((x, i) => {
            return ((i * (itemWidth) * previewCount) + startScroll)
        })
    
    
        return (
            <FlatList
                ref={flatlistRef}
                style={styles.container}
                pagingEnabled={true}
                horizontal= {true}
                decelerationRate={0}
                snapToOffsets={snapToOffsets}
                snapToAlignment={"center"}
                data={data}
                renderItem={({item, index}) => (
                    <View style={styles.view} >
                        <Text style={styles.text}>{index}</Text>
                    </View>
                )}/>
        );
    
    }
    
    
    
    export default App;
    
    
    const styles = StyleSheet.create({
        container: {
    
        },
        view: {
            marginTop: 100,
            backgroundColor: '#eee',
            width: itemWidth - 20, //20 is margin left and right
            margin: 10,
            height: 140,
            borderRadius: 10,
            justifyContent : 'center',
            alignItems : 'center',
        },
        text : {
            fontSize : 60,
            fontWeight : 'bold',
            color : '#aaa',
        },
    
    });
    
    

    update: start from zero as @Amir-Mousavi comment

    one by one try snack
    1-) comment useEffect.
    2-) set const startScroll = (itemWidth * 3/4)

    enter image description here

    three by three try snack
    1-) comment useEffect.
    2-) set const startScroll = (itemWidth * 2.75)

    enter image description here