How to get currently visible index in RN flat list

JavascriptReact NativeReact Native-FlatlistReact Native-Scrollview

Javascript Problem Overview


I have a horizontal flat list where each item is width:300 All I am trying to do is to get index of currently visible item.

<FlatList 
            onScroll={(e) => this.handleScroll(e)} 
            horizontal={true}
            data={this.state.data}
            renderItem...

Tried this:

handleScroll(event) {
    let index = Math.ceil(
      event.nativeEvent.contentOffset.x / 300
    );

And something like this:

handleScroll(event) {
  let contentOffset = event.nativeEvent.contentOffset;
  let index = Math.floor(contentOffset.x / 300);

but nothing is accurate I always get one index up or one index down.
What am I doing wrong and how to get correct current index in flat list?

For example I get to list item that is 8th in a list but I get index 9 or 10.
enter image description here

Javascript Solutions


Solution 1 - Javascript

UPD. thanks to @ch271828n for pointing that onViewableItemsChanged shouldn`t be updated

You can use FlatList onViewableItemsChanged prop to get what you want.

class Test extends React.Component {
  onViewableItemsChanged = ({ viewableItems, changed }) => {
    console.log("Visible items are", viewableItems);
    console.log("Changed in this iteration", changed);
  }

  render () => {
    return (
      <FlatList
        onViewableItemsChanged={this.onViewableItemsChanged }
        viewabilityConfig={{
          itemVisiblePercentThreshold: 50
        }}
      />
    )
  }
}

viewabilityConfig can help you to make whatever you want with viewability settings. In code example 50 means that item is considered visible if it is visible for more than 50 percents.

Config stucture can be found here

Solution 2 - Javascript

With related to @fzyzcjy's and @Roman's answers. In react, 16.8+ you can use useCallback to handle the changing onViewableItemsChanged on the fly is not supported error.

    function MyComponent(props) {
        const _onViewableItemsChanged = useCallback(({ viewableItems, changed }) => {
            console.log("Visible items are", viewableItems);
            console.log("Changed in this iteration", changed);
        }, []);
    
        const _viewabilityConfig = {
            itemVisiblePercentThreshold: 50
        }
    
        return <FlatList
                onViewableItemsChanged={_onViewableItemsChanged}
                viewabilityConfig={_viewabilityConfig}
                {...props}
            />;
    }

Solution 3 - Javascript

Many thanks to the most-voted answer :) However, it does not work when I try it, raising an error saying that changing onViewableItemsChanged on the fly is not supported. After some search, I find the solution here. Here is the full code that can run without error. The key point is that the two configs should be put as class properties instead of inside the render() function.

class MyComponent extends Component {  
  _onViewableItemsChanged = ({ viewableItems, changed }) => {
    console.log("Visible items are", viewableItems);
    console.log("Changed in this iteration", changed);
  };

  _viewabilityConfig = {
    itemVisiblePercentThreshold: 50
  };

  render() {
    return (
        <FlatList
          onViewableItemsChanged={this._onViewableItemsChanged}
          viewabilityConfig={this._viewabilityConfig}
          {...this.props}
        />
      </View>
    );
  }
}

Solution 4 - Javascript

this.handleScroll = (event) => {
  let yOffset = event.nativeEvent.contentOffset.y
  let contentHeight = event.nativeEvent.contentSize.height
  let value = yOffset / contentHeight
}

<FlatList onScroll={this.handleScroll} />

Get the round-off value to calculate the page-number/index.

Solution 5 - Javascript

In your case, I guess, you might ignore the padding or margin of items. The contentOffsetX or contentOffsetY should be firstViewableItemIndex * (itemWidth + item padding or margin).

enter image description here

As other answers, onViewableItemsChanged would be a better choice to meet your requirement. I wrote an article about how to use it and how it is implemented. https://suelan.github.io/2020/01/21/onViewableItemsChanged/

Solution 6 - Javascript

A bit late, but for me was different with functional components...

If you are using functional component do it like:

  const onScroll = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
    const slideSize = event.nativeEvent.layoutMeasurement.width;
    const index = event.nativeEvent.contentOffset.x / slideSize;
    const roundIndex = Math.round(index);
    console.log("roundIndex:", roundIndex);
  }, []);

Then on your FlatList

<FlatList
      data={[2, 3]}
      horizontal={true}
      pagingEnabled={true}
      style={{ flex: 1 }}
      showsHorizontalScrollIndicator={false}
      renderItem={renderItem}
      onScroll={onScroll}
    />

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Question1110View Question on Stackoverflow
Solution 1 - JavascriptRoman OsypovView Answer on Stackoverflow
Solution 2 - JavascripttiranView Answer on Stackoverflow
Solution 3 - Javascriptch271828nView Answer on Stackoverflow
Solution 4 - JavascriptSujitView Answer on Stackoverflow
Solution 5 - JavascriptRY_ ZhengView Answer on Stackoverflow
Solution 6 - JavascriptmanuelBetancurtView Answer on Stackoverflow