skip to Main Content

How can I implement a FlatList, in which I have items with dynamic heights and when I scroll to next item it should always snap on top of the screen.

Basically something like

https://www.youtube.com/watch?v=pTtxhuThMew&ab_channel=CatalinMiron
in this video but my items will be listed vertically and I wan’t to achieve this not once i click to a Pressable but on scroll.

Let’s say you have list of images with different heights and each time you scroll, you wan’t the next item you scrolling to, snap to top of the screen.

2

Answers


  1. Chosen as BEST ANSWER
          onViewableItemsChanged={info => {
            if (info.changed.length > 0 && info.changed[info.changed.length - 1].isViewable) {
              setIndex(info.changed[info.changed.length - 1].index);
            }
          }}
          viewabilityConfig={{
            viewAreaCoveragePercentThreshold: 10,
            waitForInteraction: true,
          }}
          onScrollEndDrag={() => {
            listRef.current.scrollToIndex({
              index: index,
              animated: true,
              viewOffset: insets.top,
            });
          }}
    

    meanwhile found a quick solution with following props


  2. I thought this would work:

    <FlatList
      data={items}
      renderItem={({ item, index }) => (
        <View style={item}/>
      )}      
      snapToOffsets={items.map(item=>item.height)}
    />
    

    But snapToOffsets are absolute positions, so you need to accumulate the height/width:

    // accessor allows to create snap offsets for both width and height
    const getSnapPoints = (items, accessor = 'height') => {
      return items.reduce((prev, curr) => {
        const prevPosition = prev[prev.length - 1] || 0;
    
        prev.push(curr[accessor] + prevPosition);
        return prev;
      }, []);
    };
    

    Bringing it all together

    import * as React from 'react';
    import { Text, View, StyleSheet, FlatList, Dimensions } from 'react-native';
    import Constants from 'expo-constants';
    import { colorGenerator } from '@phantom-factotum/colorutils';
    
    
    
    // generate snap points by accumulating width/height
    const getSnapPoints = (items, accessor = 'height') => {
      return items.reduce((prev, curr) => {
        const prevPosition = prev[prev.length - 1] || 0;
    
        prev.push(curr[accessor] + prevPosition);
        return prev;
      }, []);
    };
    
    const isHorizontal = false;
    
    const getRandom = (min = 0, max = 1) => {
      let range = max - min;
      return Math.random() * range + min;
    };
    
    // generate random View sizes
    const { width, height } = Dimensions.get('screen');
    const items = colorGenerator(10).map((color) => {
      return {
        height: Math.round(getRandom(200, height)),
        width: Math.round(getRandom(200, width)),
        backgroundColor: color,
      };
    });
    
    export default function App() {
      return (
        <View style={styles.container}>
          <FlatList
            data={items}
            renderItem={({ item, index }) => (
              <View style={item}>
                <Text>
                  Item {index + 1} of {items.length}
                </Text>
              </View>
            )}
            // generate snapPoints base on if isHorizontal
            snapToOffsets={getSnapPoints(items, isHorizontal ? 'width' : 'height')}
            horizontal={isHorizontal}
          />
        </View>
      );
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: 'center',
        paddingTop: Constants.statusBarHeight,
        backgroundColor: '#ecf0f1',
        padding: 8,
      },
      paragraph: {
        margin: 24,
        fontSize: 18,
        fontWeight: 'bold',
        textAlign: 'center',
      },
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search