How to scroll FlatList to some index immediately after scrolling?

Viewed 1182

How to scroll FlatList component to some index/children after dragging the FlatList?

For Example:

As we can see in Youtube/TikTok stories, when we drag the screen the next video appears immidiatly after it. So, I am implementing it with FlatList, if we drag the item below then FlatList should move to the above item/index. So, what I am doing is that storing the currently displayed index and on onScrollEndDrag prop I am checking the position of Y, and accordingly run scrollToIndex function, but it's not working.

Reason

Because while scrolling after drag, FlatList ignores the scrollToIndex function.

Is their anyone to help me out of it???

import React from 'react';
import { View, Text, StyleSheet, AppState, FlatList, Animated, Dimensions } from 'react-native';
import fetchDataFromDirectory from '../data/fetchDataFromWhatsApp';
import PlayerVideo from '../components/VideoPlayer';
import Image from '../components/Image';

const { width, height } = Dimensions.get('window');

const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);

class VideoScreen extends React.Component {
    state = {
        pdfInfo: [], //[{id, name, path},...]
        appState: '',
        viewableIndex: 0
    }

    fetchData = async () => {
        const data = await fetchDataFromDirectory('videos');
        this.setState({ pdfInfo: data.pdfInfo });
    }

    componentDidUpdate(prevProps, prevState) {
        if (this.state.pdfInfo.length > this.dataLength) { //We are seeing if we need to scroll to top or not
            this.dataLength = this.state.pdfInfo.length;
            try {
                this.list.scrollToIndex({ animated: true, index: 0, viewPosition: 0 })
            } catch (err) {

            }
        }
    }

    handleAppStateChange = (nextAppState) => {
        //the app from background to front
        if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') {
            this.fetchData();
        }
        //save the appState
        this.setState({ appState: nextAppState });
    }

    componentDidMount() {
        this.videoHeight = height;
        this.dataLength = 0;
        this.fetchData();
        AppState.addEventListener('change', this.handleAppStateChange);
    }

    onViewableItemsChanged = ({ viewableItems, changed }) => {
        // console.log("Visible items are", viewableItems);
        // console.log("Changed in this iteration", changed);
        try {
            this.setState({ viewableIndex: viewableItems[0]['index'] })
        } catch (err) {

        }
    }

    componentWillUnmount() {
        AppState.removeEventListener('change', this.handleAppStateChange)
    }

    render() {
        return <AnimatedFlatList
            onLayout={(e) => {
                const { height } = e.nativeEvent.layout;
                this.videoHeight = height;
            }}
            // onResponderRelease={e => {console.log(e.nativeEvent.pageY)}}
            // onResponderRelease={(e) => console.log(e.nativeEvent.)}
            // onScrollBeginDrag
            // snapToAlignment={'top'}
            // onMoveShouldSetResponderCapture={(e) => {e.nativeEvent.}}
            // decelerationRate={'fast'}
            decelerationRate={'fast'}
            scrollEventThrottle={16}
            // onScroll={(e) => console.log('+++++++++++++++++',Object.keys(e), e.nativeEvent)}
            onScrollEndDrag={(e) => {
                // this.list.setNativeProps({ scrollEnabled: false })
                console.log(e.nativeEvent)
                if (e.nativeEvent.velocity.y > 0.1) {
                    console.log('go to above')
                    this.list.scrollToIndex({animated: true, index: this.state.viewableIndex - 1, viewPosition: 0})
                } else if (e.nativeEvent.velocity.y < -0.9){
                    console.log('go to below')
                    this.list.scrollToIndex({animated: true, index: this.state.viewableIndex + 1, viewPosition: 0})
                } 
                // if (e.nativeEvent.velocity.y < 0.1000 && e.nativeEvent.velocity.y >= 0) {
                //     this.list.scrollToIndex({animated: true, index: this.state.viewableIndex, viewPosition: 0})
                // }
                // else if (e.nativeEvent.velocity.y < -0.1000 && e.nativeEvent.velocity.y < 0) {
                //     this.list.scrollToIndex({animated: true, index: this.state.viewableIndex, viewPosition: 0})
                // }
                // this.list.setNativeProps({ scrollEnabled: true })
                console.log('h1')
            }}
            viewabilityConfig={{
                // itemVisiblePercentThreshold: 90,
                viewAreaCoveragePercentThreshold: 60
            }}
            // extraData={this.state.viewableIndex}
            onViewableItemsChanged={this.onViewableItemsChanged}
            // scr
            contentContainerStyle={styles.screen}
            data={this.state.pdfInfo}
            keyExtractor={item => item.id}
            ref={ref => this.list = ref}
            renderItem={({ item, index }) => {
                // console.log(index)
                return <PlayerVideo
                    source={item.path}
                    refList={this.list}
                    height={this.videoHeight}
                    index={index}
                    isViewable={this.state.viewableIndex == index ? true : false} />
            }}
                </View>
            }}
        />
    }
}

export default VideoScreen;

const styles = StyleSheet.create({
    screen: {
        backgroundColor: '#111212',
        // flex: 1
    }
})

1 Answers

First add a ref flatListRef to the flatlist:

<Flatlist
     ref={(ref) => this.flatListRef = ref}
     data={data}
     keyExtractor={keyExtractor}
     renderItem={renderItem}
     onScrollBeginDrag={onBeginScroll}/>
/>

Then define the onScrollBeginDrag function as below to scroll to a specific index:

onBeginScroll = () => {
    this.flatListRef._listRef._scrollRef.scrollToIndex({ animating: true, index: [YOUR_INDEX_HERE] });
}

yes you should access the element _listRef then _scrollRef then call the scrollToIndex


  • react-native 0.64.1
  • react 17.0.2

Related