React-native flatlist item layout should adapt device orientation change

733 Views Asked by At

So I am creating flatlist of images. Which shows only one image at a time on the screen. Now if I rotate the device then the image which is in the landscape should be shown in full screen in landscape mode of the device.

I can not attach the code because it is too large. If anyone can give an example of this it will be great.

Just imagine this my rendetitem view of flatlist, here in place of text I have an image and it should be seen in landscape mode as device orientation change to landscape.

It should not affect the pagination of flatlist when device orientation comes again in portrait.

enter image description here

3

There are 3 best solutions below

9
Louay Sleman On BEST ANSWER

To update the dimensions of the images in the FlatList when the device is rotated to landscape mode, you can add an event listener to the Dimensions API and update the state with the new dimensions like this:

  const [imageDimensions, setImageDimensions] = useState({
    width: Dimensions.get('window').width - 40,
    height: Dimensions.get('window').height - 40,
  });

   useEffect(() => {
    const updateDimensions = () => {
      setImageDimensions({
        width: Dimensions.get('window').width - 40,
        height: Dimensions.get('window').height - 40,
      });
      setTimeout(() => {
        flatListRef.current?.scrollToIndex({ animated: false, index: selectedIndex });
      }, 100);
    };

    const dimensionsHandler= Dimensions.addEventListener('change', updateDimensions);

    return () => {
     dimensionsHandler.remove();
    };
  }, [selectedIndex]);

// Image code
   <Image source={{ uri: 'https://url' }} style={imageDimensions} />

Make sure that your flatList have this viewability Config


 const viewabilityConfig = useRef({
    minimumViewTime: 100,
    itemVisiblePercentThreshold: '90%',
  }).current;


  <FlatList
   ref={flatListRef}
   data={data}
   renderItem={renderItem}
   keyExtractor={(item) => item.id.toString()}
   horizontal
   initialScrollIndex={selectedIndex}
   onViewableItemsChanged={onViewableItemsChanged}
   viewabilityConfig={viewabilityConfig}
   pagingEnabled />

You can check this working example from here.

Hope it helps :D

0
128KB On

I've struggled with this issue for so so long. This code does what you need it to, but there are potential lurking bugs leaving visibleIndex out of the useEffect dependency array.

  const {height, width} = useWindowDimensions();
  const [visibleIndex, setVisibleIndex] = useState(0);
  const flatListRef = useRef(null);

  useEffect(() => {
    if (data.length > 0 && flatListRef.current) {
      flatListRef.current.scrollToIndex({index: visibleIndex, animated: false});
    }
  }, [width, data]);

  const getItemLayout = (data, index) => ({
    length: width,
    offset: width * index,
    index,
  });

  const handleScrollEnd = event => {
    const newIndex = Math.round(event.nativeEvent.contentOffset.x / width);
    setVisibleIndex(newIndex);
  };

  return (
    <View style={styles.container}>
      <FlatList
        key={width}
        ref={flatListRef}
        data={data}
        renderItem={renderItem}
        keyExtractor={(_, index) => index.toString()}
        getItemLayout={getItemLayout}
        pagingEnabled={true}
        horizontal
        onMomentumScrollEnd={handleScrollEnd}
        showsHorizontalScrollIndicator={false}
        onEndReached={fetchMoreData}
        onEndReachedThreshold={0.5}
      />
    </View>
  );
} 
0
kroczku On

I used a mixed solution. In my case, I had a slider where one image was visible at a time. That's why I wait until the end of the animation when only one element remains visible. Then I set currentIndex. Then I use onContentSizeChange because it uses onLayout, so there are no races and I don't have to use timouts.

const handleOnViewableItemsChanged = useCallback(
  ({ viewableItems }: { viewableItems: ViewToken[] }) => {
    if (viewableItems.length === 1 && viewableItems[0].index) {
      currentIndex.current = viewableItems[0].index;
    }
  },
  [],
);

const onContentSizeChange = () => {
  if (listRef.current) {
    listRef.current.scrollToIndex({
      index: currentIndex.current,
      animated: false,
    });
  }};