1

I'm trying to create a carousel with infinite looping on finite set of data using FlatList and am using ScrollToIndex to scroll to start when I reach the end and scroll to the end when I reach the start.

However, scrollToIndex causes this weird flashing behaviour when it scrolls from the last banner back to the first, and only on Android (although IOS also has some minor unstable behaviour). Here's the rough code.


const Carousel = () => {

    const [activeBanner, setActiveBanner] = useState(0);
    const scrollX = useRef(new Animated.Value(0)).current;
    const listRef = useRef<RNFlatList>(null);

    const goToIndex = (index: number, animated: boolean) => {
        listRef.current?.scrollToIndex({
          index,
          animated,
        });
      };

    const handleScroll = (event: any) => {
        const { contentOffset } = event.nativeEvent;
        const currentIndex = Math.floor((contentOffset.x - 50) / FULL_BANNER_WIDTH);
        if (
          currentIndex >= banners.length &&
          contentOffset.x >= ACTIVE_BANNER_WIDTH * (banners.length + 1) - 20
        ) {
          goToIndex(1, false);
        } else if (currentIndex <= 0 && contentOffset.x <= 0) {
          goToIndex(banners.length, false);
        }
    }

    const itemLayout = (_data: any, index: number) => ({
        length: FULL_BANNER_WIDTH,
        offset: FULL_BANNER_WIDTH * index,
        index: index,
    });

    const onViewableItemsChangedHandler = (info: any) => {
        if (info.viewableItems.length > 0) {
          setActiveBanner(info.viewableItems[0].index - 1);
        } else {
          setActiveBanner(0);
        }
    };

    const viewConfigRef = React.useRef([
        {
          viewabilityConfig: {
            waitForInteraction: false,
            itemVisiblePercentThreshold: 100,
            minimumViewTime: 0,
          },
          onViewableItemsChanged: onViewableItemsChangedHandler,
        },
    ]);

    const flatlistMemo = React.useMemo(() => {
        return (
          <Animated.FlatList
            decelerationRate={0.9}
            disableIntervalMomentum={true}
            disableScrollViewPanResponder={true}
            snapToInterval={FULL_BANNER_WIDTH}
            horizontal={true}
            showsHorizontalScrollIndicator={false}
            ref={listRef}
            data={renderItem}
            renderItem={renderItems}
            keyExtractor={keyExtractor}
            onScroll={Animated.event(
              [{ nativeEvent: { contentOffset: { x: scrollX } } }],
              { useNativeDriver: true, listener: event => handleScroll(event) }
            )}
            scrollEventThrottle={16}
            onScrollToIndexFailed={onScrollFail}
            getItemLayout={itemLayout}
            onScrollBeginDrag={() => setIsDragging(true)}
            onScrollEndDrag={() => setIsDragging(false)}
            initialScrollIndex={1}
            contentContainerStyle={[
              styles.flatListContainer,
              {
                marginLeft: -HIDDEN_BANNER_PORTION,
              },
            ]}
            viewabilityConfigCallbackPairs={viewConfigRef.current}
          />
        );
      }, [renderItem]);
    
    return (
      <View>{flatlistMemo}</View>
    )
}

Things I have tried:

  • removing use of setState to track active banner

  • using/removing flatlist memoization

  • key extractor

  • removed animations

  • configured windowsize, etc

  • changed to ScrollView / ScrollTo

tammi
  • 143
  • 2
  • 8

0 Answers0