43

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

1110
  • 7,801
  • 45
  • 156
  • 303

5 Answers5

70

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

ch271828n
  • 5,683
  • 3
  • 24
  • 44
Roman Osypov
  • 1,051
  • 12
  • 10
  • 1
    But how I can know which data to use. Because item is 300p wide and if it's in the middle I will see 20p of left and right items, ok here I can get middle one. But if there are two items how to pick the next to get it's data. – 1110 Aug 24 '17 at 19:35
  • This callback returns all three items as visible. Only fully gone items will be marked as not visible. – Roman Osypov Aug 24 '17 at 19:39
  • I updated the question with image. When I got two items visible like on image. I need to get only one item data (possibly the one that is more visible than another). Is it possible with this callback somehow? – 1110 Aug 24 '17 at 19:44
  • I updated my answer. I think you can experiment with values now and get what you really want – Roman Osypov Aug 24 '17 at 19:55
  • @RomanOsypov Thanks, works for me. But I want to know, is there any function I can call to get the `viewableItems`? As I think `onViewableItemsChanged` will be called multi times upon scrolling events. Hence want to be more efficient. Thanks in advance. – LiuWenbin_NO. Mar 29 '18 at 09:18
  • I think you can use throttle or debounce for the callback on this event. – Roman Osypov Mar 29 '18 at 09:23
  • 4
    In react native 0.54 says: **ExceptionsManager.js:65 Invariant Violation: Changing onViewableItemsChanged on the fly is not supported** – S.Hossein Asadollahi Apr 09 '18 at 16:23
  • @S.HosseinAsadollahi I believe you should move arrow function from FlatList props and provide in immutble manner. – Roman Osypov Apr 10 '18 at 18:54
  • I tried this for hours on an Expo 37.0.3 project running in an Android simulator. It won't fire at all. `onScroll` fires fine, so it's something specific with `onViewableItemsChanged`, perhaps with Android or this version of Expo. – pmont May 15 '20 at 06:25
  • @Roman Osypov I am using functional component and used state to save index the code working with the console.log() but when itry to add the set state code it throw error as 'Error: Changing viewabilityConfig on the fly is not supported' – kViN May 29 '20 at 14:52
13

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>
    );
  }
}
ch271828n
  • 5,683
  • 3
  • 24
  • 44
12

With related to @fzyzcjy's and @Roman's answers. In react, 16.8+ you can use uscCallback 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}
        />;
}
tiran
  • 2,151
  • 1
  • 12
  • 28
9
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.

Chicken Suop
  • 1,047
  • 3
  • 12
  • 32
Sujit
  • 478
  • 5
  • 19
6

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/

RY_ Zheng
  • 1,465
  • 17
  • 25