Amir-Mousavi
Amir-Mousavi

Reputation: 4563

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

Upvotes: 6

Views: 10692

Answers (2)

Ahmed Gaber
Ahmed Gaber

Reputation: 3986

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

Upvotes: 10

Alen.Toma
Alen.Toma

Reputation: 4870

Ok after much work and testing I finally was able to fix this.

snapToInterval have to snap to interval a full length of the screen.

if you use pWidth *3 it wont work. Now you may ask why, I really do not understand , it may have something to do with float values.

But if you use snapToInterval={Dimensions.get('window').width} it should work.

Have a look at snack example

Upvotes: 3

Related Questions