1

I have flatlist horizontal like below:

const DATA = [
  {
    id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
    title: 'First Item',
  },
  {
    id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
    title: 'Second Item',
  },
  {
    id: '58694a0f-3da1-471f-bd96-145571e29d72',
    title: 'Third Item',
  },
    {
    id: 'bd7acbea-c1b1-46c2-aed5-3ad353abb28ba',
    title: 'Fourth Item',
  },
  {
    id: '3ac68afc-c605-48d3-a4f8-fbd291aa97f63',
    title: 'Fifth Item',
  },
  {
    id: '58694a0f-3da1-471f-bd961-145571e29d72',
    title: 'Sixth Item',
  },
];

const Item = ({ title }) => (
  <View style={styles.item}>
    <Text style={styles.title}>{title}</Text>
  </View>
);

const App = () => {
  const renderItem = ({ item }) => (
    <Item title={item.title} />
  );

  return (
    <SafeAreaView style={styles.container}>
      <FlatList
        horizontal
        data={DATA}
        renderItem={renderItem}
        keyExtractor={item => item.id}
      />
    </SafeAreaView>
  );
}

Whenever Item entered the viewport, I want to add animation to that element. I can get X and Y position of scroll with onScroll, now how do I get the positions of items to check if its in view port or if it went away from viewport...

halfer
  • 19,824
  • 17
  • 99
  • 186
Gracie williams
  • 1,287
  • 2
  • 16
  • 39

2 Answers2

2

Use onViewableItemsChanged this is called when the items in the flatlist changes.

const handleViewableItemsChanged = (viewableItems, changed) => {}

<Flatlist
  ...
  onViewableItemsChanged={handleViewableItemsChanged}
Junius L
  • 15,881
  • 6
  • 52
  • 96
  • Thank you , is there anyway to check if flat list parent came into view.. I have multiple horziontal scrollable cards in page. – Gracie williams Jun 14 '21 at 07:44
  • @Graciewilliams `viewableItems` will be the an array containing all the `DATA` items on screen. You filter viewableItems for the id of the specific parent item. You should also beware that there a few extra things you have to setup in order to be able to use onViewableItemsChanged (I can't give the specifics cuz https://react-native.dev is down in my location) – PhantomSpooks Jun 14 '21 at 11:32
  • @PhantomSpooks , how do i access an particular element like $("#elem") in react native after i filter it via viewableItems ... like how do I add style to it ? – Gracie williams Jun 14 '21 at 12:37
  • I don't think you can directly access the element. But you can change `Item` so that it render differently based on the value of item. For an example, if you added ` – PhantomSpooks Jun 14 '21 at 13:32
  • I've posted an answer – PhantomSpooks Jun 14 '21 at 17:37
1

I implemented the basic fade in/out animation example into the Item component. Whether it fades out or in is decided by the prop isViewable

// Item.js
const Item = (props) => { 
  const {
    item:{title, isViewable}
  } = props
  /*
  I copied and pasted the basic animation example from the react-native dev page
  */
  const fadeAnim = useRef(new Animated.Value(1)).current;
  const fadeIn = () => {
    Animated.timing(fadeAnim, {
      toValue: 1,
      duration: 1000,
      useNativeDriver:false
    }).start();
  };
  const fadeOut = () => {
    Animated.timing(fadeAnim, {
      toValue: 0,
      duration: 1500,
      useNativeDriver:false
    }).start();
  };
  /*  end of animation example*/
  
  // fade in/out base on if isViewable
  if(isViewable || isViewable == 0)
    fadeIn()
  else
    fadeOut()
  const animation = {opacity:fadeAnim}
  return (
    //add animation to Animated.View
    <Animated.View style={[style.itemContainer,animation]}>
      <View style={style.item}>
        <Text style={style.title}>{title}</Text>
      </View>
    </Animated.View>
  );
}

Create a FlatListWrapper (to avoid the onViewableItemChange on fly error). By doing this, as long as you don't make changes to FlatListWrapper, you won't get the on the fly error:

// FlatListWrapper.js
const FlatListWrapper = (props) => {

  // useRef to avoid onViewableItemsChange on fly error
  const viewabilityConfig = useRef({
    // useRef to try to counter the view rerender thing
    itemVisiblePercentThreshold:80
  }).current;
  // wrapped handleViewChange in useCallback to try to handle the onViewableItemsChange on fly error
  const onViewChange = useCallback(props.onViewableItemsChanged,[])
  return (
      <View style={style.flatlistContainer}>
        <FlatList
          {...props}
          horizontal={true}
          onViewableItemsChanged={onViewChange}
        />
      </View>
  );
}
const style = StyleSheet.create({
 
  flatlistContainer:{
    borderWidth:1,
    borderColor:'red',
    width:'50%',
    height:40
  },
 
// main FlatList component
const FlatListAnimation = () => {

  // store the indices of the viewableItmes
  const [ viewableItemsIndices, setViewableItemsIndices ] = useState([]);
  
  return (
    <SafeAreaView style={style.container}>
      <FlatListWrapper
        horizontal={true}
        //{/*give each data item an isViewable prop*/}
        data={DATA.map((item,i)=>{
          item.isViewable=viewableItemsIndices.find(ix=>ix == i)
          return item
        })}
        renderItem={item=><Item {...item}/>}
        keyExtractor={item => item.id}
        onViewableItemsChanged={({viewableItems, changed})=>{
          // set viewableItemIndices to the indices when view change
          setViewableItemsIndices(viewableItems.map(item=>item.index))
        }}
        //{/*config that decides when an item is viewable*/} 
        viewabilityConfig={{itemVisiblePercentThreshold:80}}
        extraData={viewableItemsIndices}
      />
     {/* Extra stuff that just tells you what items should be visible*/}
      <Text>Items that should be visible:</Text>
      {viewableItemsIndices.map(i=><Text>  {DATA[i].title}</Text>)}
    </SafeAreaView>
  );
}
const style = StyleSheet.create({
  container:{
    padding:10,
    alignItems:'center'
  },
  flatlistContainer:{
    borderWidth:1,
    borderColor:'red',
    width:'50%',
    height:40
  },
  item:{
    borderWidth:1,
    padding:5,
  },
  itemContainer:{
    padding:5,
  }
})

By wrapping your FlatList in a separate file, you won't encounter the "onViewableItemsChange on the fly" error as long as you don't modify FlatListWrapper.js

halfer
  • 19,824
  • 17
  • 99
  • 186
PhantomSpooks
  • 2,877
  • 2
  • 8
  • 13
  • hi , I am getting this error - Invariant Violation: Changing onViewableItemsChanged on the fly is not supported – Gracie williams Jun 14 '21 at 18:17
  • This error occurs because FlatLists with functional components have some gimmicks. (This is the re-render error I was talking about). If you make changes to the file containing onViewableItemsChanged, then this error is thrown. You could either reload the app to erase this error, or create a FlatList wrapper component in a separate file – PhantomSpooks Jun 15 '21 at 00:13
  • I've updated the answer with to include a WrapperFlatList to avoid the error – PhantomSpooks Jun 15 '21 at 00:37
  • Thank you accepted it,, can u please check this ? I want to add animation to items -- https://stackoverflow.com/questions/67976326/add-animatable-items-to-flatlist-horizontal-items – Gracie williams Jun 15 '21 at 11:01