Skip to content

Commit 58cf837

Browse files
authored
fix: flashlist inverted handling (#49)
Highlights: - Fixed FlashList inverted handling since it was refactored in v2
1 parent e7cb7c9 commit 58cf837

File tree

4 files changed

+49
-11
lines changed

4 files changed

+49
-11
lines changed

example/src/screens/usage/FlatList.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ const FlatList: React.FC<FlatListUsageScreenNavigationProps> = () => {
8383
data={data}
8484
renderItem={renderItem}
8585
windowSize={10}
86+
automaticallyAdjustsScrollIndicatorInsets={false}
8687
getItemLayout={(_, index) => ({ index, length: ITEM_HEIGHT, offset: index * ITEM_HEIGHT })}
8788
initialNumToRender={50}
8889
maxToRenderPerBatch={100}

example/src/screens/usage/Inverted.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@ import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
33
import { useNavigation } from '@react-navigation/native';
44
import { randParagraph, randUuid } from '@ngneat/falso';
55
import { BlurView } from 'expo-blur';
6-
import { useSafeAreaInsets } from 'react-native-safe-area-context';
76
import { StatusBar } from 'expo-status-bar';
87
import {
98
Header,
109
ScrollHeaderProps,
11-
FlatListWithHeaders,
1210
SurfaceComponentProps,
11+
FlashListWithHeaders,
1312
} from '@codeherence/react-native-header';
1413
import { Avatar, BackButton } from '../../components';
1514
import { RANDOM_IMAGE_NUM } from '../../constants';
@@ -68,20 +67,23 @@ const ChatMessage: React.FC<ChatMessage> = ({ message, type }) => {
6867
};
6968

7069
const Inverted: React.FC<InvertedUsageScreenNavigationProps> = () => {
71-
const { bottom } = useSafeAreaInsets();
72-
7370
return (
7471
<>
7572
<StatusBar style="light" />
76-
<FlatListWithHeaders
73+
<FlashListWithHeaders
7774
data={data}
7875
renderItem={({ item }) => <ChatMessage {...item} />}
7976
HeaderComponent={HeaderComponent}
8077
keyExtractor={(item) => item.id}
81-
inverted
78+
// inverted
8279
absoluteHeader
8380
containerStyle={styles.container}
84-
contentContainerStyle={[styles.contentContainer, { paddingTop: bottom }]}
81+
maintainVisibleContentPosition={{
82+
startRenderingFromBottom: true,
83+
}}
84+
automaticallyAdjustsScrollIndicatorInsets={false}
85+
// eslint-disable-next-line react-native/no-inline-styles
86+
contentContainerStyle={{ paddingHorizontal: 12 }}
8587
showsVerticalScrollIndicator
8688
indicatorStyle={'white'}
8789
/>
@@ -93,6 +95,7 @@ export default Inverted;
9395

9496
const styles = StyleSheet.create({
9597
container: {
98+
flex: 1,
9699
backgroundColor: 'black',
97100
},
98101
contentContainer: {

src/components/containers/FlashList.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import React, { useImperativeHandle } from 'react';
1+
import React, { useImperativeHandle, useMemo } from 'react';
22
import { StyleSheet, View } from 'react-native';
33
import { useSafeAreaInsets } from 'react-native-safe-area-context';
4-
import Animated, { AnimatedProps, useAnimatedRef } from 'react-native-reanimated';
4+
import Animated, { AnimatedProps, isSharedValue, useAnimatedRef } from 'react-native-reanimated';
55
import { FlashList, FlashListProps, FlashListRef } from '@shopify/flash-list';
66

77
import type { SharedScrollContainerProps } from '.';
@@ -60,6 +60,17 @@ const FlashListWithHeadersInputComp = <ItemT extends any = any>(
6060
const scrollRef = useAnimatedRef<any>();
6161
useImperativeHandle(ref, () => scrollRef.current);
6262

63+
const { maintainVisibleContentPosition } = rest;
64+
const inverted = useMemo(() => {
65+
if (maintainVisibleContentPosition === undefined) return false;
66+
if (isSharedValue(maintainVisibleContentPosition)) return false;
67+
68+
return (
69+
(maintainVisibleContentPosition as FlashListProps<ItemT>['maintainVisibleContentPosition'])
70+
?.startRenderingFromBottom ?? false
71+
);
72+
}, [maintainVisibleContentPosition]);
73+
6374
const {
6475
scrollY,
6576
showNavBar,
@@ -77,8 +88,9 @@ const FlashListWithHeadersInputComp = <ItemT extends any = any>(
7788
absoluteHeader,
7889
initialAbsoluteHeaderHeight,
7990
headerFadeInThreshold,
80-
inverted: false,
91+
inverted,
8192
onScrollWorklet,
93+
isFlashList: true,
8294
});
8395

8496
return (

src/components/containers/useScrollContainerLogic.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
} from 'react-native-reanimated';
1313
import { useDebouncedCallback } from 'use-debounce';
1414
import type { SharedScrollContainerProps } from './types';
15+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
1516

1617
/**
1718
* The arguments for the useScrollContainerLogic hook.
@@ -77,6 +78,12 @@ interface UseScrollContainerLogicArgs {
7778
* ensure that this function is a [worklet](https://docs.swmansion.com/react-native-reanimated/docs/2.x/fundamentals/worklets/).
7879
*/
7980
onScrollWorklet?: (evt: NativeScrollEvent) => void;
81+
/**
82+
* Whether or not the scroll container is a FlashList. This is used because FlashList actually
83+
* implements the inverted property differently and requires a different approach to compute the
84+
* scroll indicator insets and content container style.
85+
*/
86+
isFlashList?: boolean;
8087
}
8188

8289
/**
@@ -96,7 +103,9 @@ export const useScrollContainerLogic = ({
96103
headerFadeInThreshold = 1,
97104
inverted,
98105
onScrollWorklet,
106+
isFlashList = false,
99107
}: UseScrollContainerLogicArgs) => {
108+
const insets = useSafeAreaInsets();
100109
const [absoluteHeaderHeight, setAbsoluteHeaderHeight] = useState(initialAbsoluteHeaderHeight);
101110
const scrollY = useSharedValue(0);
102111
const largeHeaderHeight = useSharedValue(0);
@@ -162,6 +171,19 @@ export const useScrollContainerLogic = ({
162171
);
163172

164173
const scrollViewAdjustments = useMemo(() => {
174+
if (isFlashList) {
175+
return {
176+
scrollIndicatorInsets: {
177+
top: absoluteHeader && inverted ? absoluteHeaderHeight : 0,
178+
bottom: insets.bottom,
179+
},
180+
contentContainerStyle: {
181+
paddingTop: absoluteHeader && inverted ? absoluteHeaderHeight : 0,
182+
paddingBottom: insets.bottom,
183+
},
184+
};
185+
}
186+
165187
return {
166188
scrollIndicatorInsets: {
167189
top: absoluteHeader && !inverted ? absoluteHeaderHeight : 0,
@@ -172,7 +194,7 @@ export const useScrollContainerLogic = ({
172194
paddingBottom: absoluteHeader && inverted ? absoluteHeaderHeight : 0,
173195
},
174196
};
175-
}, [inverted, absoluteHeaderHeight, absoluteHeader]);
197+
}, [inverted, absoluteHeaderHeight, absoluteHeader, isFlashList, insets]);
176198

177199
return {
178200
scrollY,

0 commit comments

Comments
 (0)