diff --git a/example/app/_layout.tsx b/example/app/_layout.tsx
index 3a27973..2a0c39b 100644
--- a/example/app/_layout.tsx
+++ b/example/app/_layout.tsx
@@ -10,6 +10,10 @@ export default function Layout() {
name="scrollviews/vertical"
options={{ title: 'Vertical Lazy' }}
/>
+
+
);
diff --git a/example/app/scrollviews/flatlist.tsx b/example/app/scrollviews/flatlist.tsx
new file mode 100644
index 0000000..dc5b4ac
--- /dev/null
+++ b/example/app/scrollviews/flatlist.tsx
@@ -0,0 +1,124 @@
+import React, { useCallback, useRef } from 'react';
+import {
+ ImageSourcePropType,
+ ListRenderItemInfo,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+} from 'react-native';
+import {
+ LazyFlatList,
+ LazyFlatListMethods,
+} from 'react-native-lazy-scrollview';
+import { ALBUMS, PADDING_VERTICAL } from '../../constants';
+import shuffle from 'lodash/shuffle';
+import { FireOnceBlock } from '../../components/blocks/FireOnceBlock';
+import { ImageBlock } from '../../components/blocks/ImageBlock';
+import { NoLazyChild } from '../../components/blocks/NoLazyChild';
+import { Header } from '../../components/blocks/Header';
+
+const OFFSET = -50;
+
+type Block = ImageSourcePropType | 'no-lazy' | 'fire-once';
+
+export default function FlatList() {
+ const ref = useRef(null);
+
+ const data: Block[] = shuffle(
+ ALBUMS.concat(shuffle(ALBUMS)).concat(shuffle(ALBUMS))
+ );
+
+ const renderItem = useCallback(
+ ({ item: source, index }: ListRenderItemInfo) => {
+ if (source === 'no-lazy') {
+ return ;
+ }
+
+ if (source === 'fire-once') {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+ },
+ []
+ );
+
+ return (
+
+
+
+ ref.current?.scrollToStart({ animated: true })}
+ >
+ ⬆️
+
+ ref.current?.scrollToEnd({ animated: true })}
+ >
+ ⬇️
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ scrollviewContainer: {
+ flex: 1,
+ backgroundColor: '#ecf0f1',
+ },
+ offsetBar: {
+ position: 'absolute',
+ bottom: OFFSET * -1 + PADDING_VERTICAL,
+ borderBottomWidth: 1,
+ borderBottomColor: 'black',
+ left: 0,
+ right: 0,
+ opacity: 0.7,
+ height: 50,
+ justifyContent: 'flex-end',
+ },
+ offsetText: {
+ color: 'white',
+ fontSize: 18,
+ fontWeight: '600',
+ backgroundColor: '#000',
+ padding: 8,
+ alignSelf: 'flex-start',
+ },
+ arrowsContainer: {
+ top: 8,
+ right: 8,
+ position: 'absolute',
+ justifyContent: 'center',
+ flexDirection: 'row',
+ },
+ arrowButton: { marginBottom: 8 },
+ arrow: { fontSize: 32 },
+});
diff --git a/example/assets/flatlist.png b/example/assets/flatlist.png
new file mode 100644
index 0000000..31d2c0d
Binary files /dev/null and b/example/assets/flatlist.png differ
diff --git a/example/assets/header.jpg b/example/assets/header.jpg
new file mode 100644
index 0000000..747674f
Binary files /dev/null and b/example/assets/header.jpg differ
diff --git a/example/components/blocks/Header.tsx b/example/components/blocks/Header.tsx
new file mode 100644
index 0000000..fc31773
--- /dev/null
+++ b/example/components/blocks/Header.tsx
@@ -0,0 +1,86 @@
+import React, { ComponentProps, useState } from 'react';
+import {
+ Dimensions,
+ Image,
+ ImageSourcePropType,
+ StyleSheet,
+ View,
+} from 'react-native';
+import { LazyChild } from 'react-native-lazy-scrollview';
+import { SQUARE_SIZE } from '../../constants';
+import { Skeleton } from '../loaders/Skeleton';
+
+const SCREEN_WIDTH = Dimensions.get('window').width;
+
+export function Header() {
+ const [triggered, setTriggered] = useState(false);
+ const [isVisible, setIsVisible] = useState(false);
+
+ const onEnterThresholdPass = () => {
+ setTriggered(true);
+ };
+
+ const onExitThresholdPass = () => {
+ setTriggered(false);
+ };
+
+ const onVisibilityEnter = () => {
+ setIsVisible(true);
+ };
+
+ const onVisibilityExit = () => {
+ setIsVisible(false);
+ };
+
+ return (
+
+
+
+
+
+
+ {!isVisible ? : null}
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ alignSelf: 'center',
+ width: SCREEN_WIDTH,
+ },
+ contentContainer: {
+ width: SCREEN_WIDTH,
+ height: SCREEN_WIDTH * 1.25,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ image: {
+ width: SCREEN_WIDTH,
+ height: SCREEN_WIDTH * 1.25,
+ resizeMode: 'cover',
+ },
+ percentTextWrapper: {
+ position: 'absolute',
+ top: 0,
+ right: 0,
+ bottom: 0,
+ left: 0,
+ backgroundColor: 'rgba(255, 255, 255, 0.5)',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+});
diff --git a/example/components/cards/FlatListCard.tsx b/example/components/cards/FlatListCard.tsx
new file mode 100644
index 0000000..d4450dc
--- /dev/null
+++ b/example/components/cards/FlatListCard.tsx
@@ -0,0 +1,29 @@
+import React, { useEffect } from 'react';
+import {
+ interpolate,
+ useAnimatedStyle,
+ useSharedValue,
+ withRepeat,
+ withTiming,
+} from 'react-native-reanimated';
+import { FLATLIST } from '../../constants';
+import { Card } from './Card';
+
+export function FlatListCard() {
+ const animation = useSharedValue(0);
+
+ useEffect(() => {
+ animation.value = withRepeat(withTiming(1, { duration: 1000 }), -1, true);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+ const animatedStyle = useAnimatedStyle(() => {
+ return {
+ marginRight: 16,
+ transform: [
+ { translateY: interpolate(animation.value, [0, 1], [-20, 20]) },
+ ],
+ };
+ });
+
+ return ;
+}
diff --git a/example/constants.ts b/example/constants.ts
index 9c86c49..7208bac 100644
--- a/example/constants.ts
+++ b/example/constants.ts
@@ -44,6 +44,15 @@ export const VERTICAL = {
image: require('./assets/vertical.png'),
};
+export const FLATLIST = {
+ name: 'flatlist',
+ title: 'Lazy FlatList',
+ color: '#f8a5c2',
+ description:
+ 'Less use cases than LazyScrollView, but still useful for situations where you need to measure headers or footers, or have specific cells react granularly to scroll visibility. Not recommended to wrap every item in LazyChild.',
+ image: require('./assets/flatlist.png'),
+};
+
export const HORIZONTAL = {
name: 'horizontal',
title: 'Horizontal Lazy ScrollView',