React-Native scroll to top with Flatlist
Asked Answered
S

5

61

I'm having a lot of trouble scrolling to the top of my Flatlist so any help would be greatly appreciated!

Essentially it fetches the first 5 items from firebase, then when onEndReached is called we append the next 5 items to the list:

data: [...this.state.data, ...results]

For now I have a refresh button at the top of my view that does the following:

this.flatListRef.scrollToOffset({ animated: true, y: 0 });

If i click this when the first 5 items are rendered it scrolls to the top of the list as expected. The issue only occurs after the list has been appended to (I guess the items are off view?).

I have also tried 'ScrollToItem' however I'm guessing this doesn't work due to the following from React Native docs:

Note: Cannot scroll to locations outside the render window without specifying the getItemLayout prop.

Can anyone explain what is happening or know what I am doing wrong?

Thank you in advance!

getItemLayout: (not entirely sure what this does or how to work out length & offset etc)

getItemLayout = (data, index) => (
{ length: 50, offset: 50 * index, index }
)

return (
  <View>
    <FlatList
      ref={(ref) => { this.flatListRef = ref; }}
      onScroll={this.handleScroll}
      data={this.state.data}
      keyExtractor={item => item.key}
      ListFooterComponent={this.renderFooter()}
      onRefresh={this.handleRefresh}
      refreshing={this.state.newRefresh}
      onEndReached={this.handleEndRefresh}
      onEndReachedThreshold={0.05}
      getItemLayout={this.getItemLayout}
      renderItem={this.renderItem}
    />
    {this.state.refreshAvailable ? this.renderRefreshButton() : null}
  </View>
);
Soekarno answered 20/5, 2018 at 15:50 Comment(0)
C
105

The correct syntax is

this.flatListRef.scrollToOffset({ animated: true, offset: 0 });

and you can also use

scrollToIndex

Coelenterate answered 27/8, 2018 at 17:8 Comment(3)
Apologies it has been so long but I want to mark this as the correct answer. It didn't work previously so may have been a bug in one of the older RN versions, but now I am on v0.57 it is working perfectly.Soekarno
How to do this with a Animated FlatList? I don't see either scrollToOffset nor scrollToIndexRoquefort
this.flatListRef?.current?.scrollToOffset({ animated: true, offset: 0 }); works for meMedley
B
51

Just in case someone is lost on how to do this with hooks, here is an example

function MyComponent() {
    const flatListRef = React.useRef()

    const toTop = () => {
        // use current
        flatListRef.current.scrollToOffset({ animated: true, offset: 0 })
    }

    return (    
        <FlatList
            ref={flatListRef}
            data={...}
            ...
        />
    )
}

The main difference is that you access it by .current

Beachhead answered 24/10, 2019 at 23:24 Comment(0)
C
18

FOR REACT HOOKS

  1. import React, {useRef} from 'react'
  2. declare it -> const flatListRef = useRef()
  3. set it like ref={flatListRef}
  4. call it like flatListRef.current.scrollToOffset({animated: false, offset: 0})
Calgary answered 17/5, 2021 at 12:13 Comment(0)
Y
2

In this answer I have mentioned a very easy code snippet where there are 2 buttons to scroll flatlist right or left. You can use this code to achieve other use cases of programmitically scrolling flatlist.

//import
import React, { useEffect, useState, useRef, useCallback } from 'react';

//React class declaration.
const DocumentsInfo = ({ route, navigation }) => {
  
  //state variable
  const [documentsArray, setDocumentsArray] = useState({}); // array being shown in flatlist.
  const [maxVisibleIndex, setMaxVisibleIndex] = useState(0); // highest visible index currently visible.
  const [minVisibleIndex, setMinVisibleIndex] = useState(0); // lowest visible index currently visible.
  const flatListRef = useRef() // reference of flatlist.

  // callback for whenever flatlist scrolls
  const _onViewableItemsChanged = useCallback(({ viewableItems, changed }) => {
     setMaxVisibleIndex(viewableItems[viewableItems.length - 1].index);
     setMinVisibleIndex(viewableItems[0].index);
  }, []);

  // function for scrolling to top
  const scrollToTop = () => { 
    setMinVisibleIndex(0);
    setMaxVisibleIndex(0);
    flatListRef.current.scrollToIndex({ index: 0, animated: true });
  };

  // function for scrolling to bottom
  const scrollToBottom = () => { 
    let temp = documentsArray.length - 1;
    setMinVisibleIndex(temp);
    setMaxVisibleIndex(temp);
    flatListRef.current.scrollToIndex({ index: temp, animated: true });
  };

  // function for moving flatlist left and right by 1 index
  const moveNextPreviousHorizontalFlatlist = (isNext) => {
     if (isNext) {
        let maxVisible = maxVisibleIndex + 1;
        if (maxVisible < documentsArray.length) {
           let minVisible = minVisibleIndex + 1;
           setMinVisibleIndex(minVisible);
           setMaxVisibleIndex(maxVisible);
           flatListRef.current.scrollToIndex({ index: maxVisible, animated: true });
        }
     }
     else {
        let minVisible = minVisibleIndex - 1;
        if (minVisible >= 0) {
           let maxVisible = maxVisibleIndex - 1;
           setMinVisibleIndex(minVisible);
           setMaxVisibleIndex(maxVisible);
           flatListRef.current.scrollToIndex({ index: minVisible, animated: true });
        }
      }
   };

   // UI 
   return (
       <View>
       { maxVisibleIndex != documentsArray.length - 1 &&
          <View style={styles.Refresh}>
            <TouchableOpacity onPress={() =>
               moveNextPreviousHorizontalFlatlist(true)
            }>
             <Image style={styles.Refresh} source={Refresh} />
            </TouchableOpacity>
           </View>
       }

       <FlatList
         ref={flatListRef}
         onViewableItemsChanged={_onViewableItemsChanged}
         showsHorizontalScrollIndicator={false}
         horizontal
         keyExtractor={(item, index) => item.fileName + index}
         data={documentsArray}
         renderItem={({ item, index }) => {
           return (  <DocumentListItem /> )
         }}
       />

       { minVisibleIndex != 0 &&
         <View style={styles.Refresh}>
           <TouchableOpacity onPress={() =>
              moveNextPreviousHorizontalFlatlist(false)
            }>
             <Image style={styles.Refresh} source={Refresh} />
           </TouchableOpacity>
         </View>
       }
     </View>
     );
Yeomanry answered 4/2, 2022 at 9:29 Comment(0)
S
2

Below method solved my problem. Check it out:

const flatList = useRef();
const moveToTop = () => flatList.current.scrollToIndex({ index: 0 });

return (
  <View>
    <FlatList
      ref={flatList}
      onScroll={this.handleScroll}
      data={this.state.data}
      keyExtractor={item => item.key}
      ListFooterComponent={this.renderFooter()}
      onRefresh={this.handleRefresh}
      refreshing={this.state.newRefresh}
      onEndReached={this.handleEndRefresh}
      onEndReachedThreshold={0.05}
      getItemLayout={this.getItemLayout}
      renderItem={this.renderItem}
    />
    {this.state.refreshAvailable ? this.renderRefreshButton() : null}
  </View>
);

How to add scroll to top to a FlatList in ReactNative app

Synopsis answered 14/8, 2022 at 4:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.