Skip to content

Commit 9bf39ed

Browse files
committed
feat: added flashlist as a scrollable
1 parent c941c83 commit 9bf39ed

17 files changed

+2499
-17
lines changed

example/app.json

+10-4
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@
1313
"resizeMode": "contain",
1414
"backgroundColor": "#000"
1515
},
16-
"assetBundlePatterns": [
17-
"**/*"
18-
],
16+
"assetBundlePatterns": ["**/*"],
1917
"ios": {
2018
"supportsTablet": true,
2119
"bundleIdentifier": "dev.gorhom.bottomsheet"
@@ -30,6 +28,14 @@
3028
},
3129
"web": {
3230
"favicon": "./assets/favicon.png"
33-
}
31+
},
32+
"plugins": [
33+
[
34+
"expo-asset",
35+
{
36+
"assets": ["./assets"]
37+
}
38+
]
39+
]
3440
}
3541
}

example/assets/comment.png

8.16 KB
Loading

example/assets/like.png

8.72 KB
Loading

example/assets/retweet.png

6.74 KB
Loading

example/assets/share.png

5.97 KB
Loading

example/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616
"@react-navigation/native": "^6.1.9",
1717
"@react-navigation/native-stack": "^6.9.17",
1818
"@react-navigation/stack": "^6.3.20",
19+
"@shopify/flash-list": "1.6.4",
1920
"expo": "~51.0.21",
21+
"expo-asset": "~10.0.10",
2022
"expo-blur": "~13.0.2",
23+
"expo-image": "~1.13.0",
2124
"expo-status-bar": "~1.12.1",
2225
"react": "18.2.0",
2326
"react-dom": "18.2.0",

example/src/screens/index.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ShowcaseExampleScreenSectionType } from '@gorhom/showcase-template';
1+
import type { ShowcaseExampleScreenSectionType } from '@gorhom/showcase-template';
22
import { Platform } from 'react-native';
33

44
const screens: ShowcaseExampleScreenSectionType[] = [];
@@ -163,6 +163,11 @@ if (Platform.OS !== 'web') {
163163
headerTransparent: true,
164164
},
165165
},
166+
{
167+
name: 'FlashList',
168+
slug: 'Integrations/FlashList',
169+
getScreen: () => require('./integrations/flashlist').default,
170+
},
166171
],
167172
collapsed: true,
168173
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import BottomSheet, { BottomSheetFlashList } from '@gorhom/bottom-sheet';
2+
import React, { useCallback, useMemo, useRef, useState } from 'react';
3+
import {
4+
ActivityIndicator,
5+
type ListRenderItemInfo,
6+
StyleSheet,
7+
Text,
8+
View,
9+
type ViewabilityConfig,
10+
} from 'react-native';
11+
import { Button } from '../../../components/button';
12+
import TweetContent from './TweetContent';
13+
import { tweets as tweetsData } from './data/tweets';
14+
import type Tweet from './models/Tweet';
15+
16+
const keyExtractor = (item: Tweet) => {
17+
return item.id;
18+
};
19+
20+
const snapPoints = ['25%', '50%', '90%'];
21+
22+
const FlashListExample = () => {
23+
//#region state
24+
const [tweets, setTweets] = useState(tweetsData);
25+
//#endregion
26+
27+
//#region refs
28+
const bottomSheetRef = useRef<BottomSheet>(null);
29+
const remainingTweets = useRef([...tweetsData].splice(10, tweetsData.length));
30+
const viewabilityConfig = useRef<ViewabilityConfig>({
31+
waitForInteraction: true,
32+
itemVisiblePercentThreshold: 50,
33+
minimumViewTime: 1000,
34+
}).current;
35+
//#endregion
36+
37+
const handleOnEndReached = useCallback(() => {
38+
setTimeout(() => {
39+
setTweets([...tweets, ...remainingTweets.current.splice(0, 10)]);
40+
}, 1000);
41+
}, [tweets]);
42+
const handleSnapPress = useCallback((index: number) => {
43+
bottomSheetRef.current?.snapToIndex(index);
44+
}, []);
45+
const handleExpandPress = useCallback(() => {
46+
bottomSheetRef.current?.expand();
47+
}, []);
48+
const handleCollapsePress = useCallback(() => {
49+
bottomSheetRef.current?.collapse();
50+
}, []);
51+
const handleClosePress = useCallback(() => {
52+
bottomSheetRef.current?.close();
53+
}, []);
54+
55+
//#region render
56+
const renderItem = useCallback(
57+
({ item }: ListRenderItemInfo<Tweet>) => <TweetContent tweet={item} />,
58+
[]
59+
);
60+
const renderFooter = useMemo(
61+
() => <Footer isLoading={tweets.length !== tweetsData.length} />,
62+
[tweets]
63+
);
64+
return (
65+
<View style={styles.container}>
66+
<Button label="Snap To 90%" onPress={() => handleSnapPress(2)} />
67+
<Button label="Snap To 50%" onPress={() => handleSnapPress(1)} />
68+
<Button label="Snap To 25%" onPress={() => handleSnapPress(0)} />
69+
<Button label="Expand" onPress={handleExpandPress} />
70+
<Button label="Collapse" onPress={handleCollapsePress} />
71+
<Button label="Close" onPress={handleClosePress} />
72+
<BottomSheet
73+
ref={bottomSheetRef}
74+
snapPoints={snapPoints}
75+
enableDynamicSizing={false}
76+
>
77+
<BottomSheetFlashList
78+
keyExtractor={keyExtractor}
79+
renderItem={renderItem}
80+
onEndReached={handleOnEndReached}
81+
ListFooterComponent={renderFooter}
82+
ListEmptyComponent={Empty}
83+
estimatedItemSize={150}
84+
ItemSeparatorComponent={Divider}
85+
data={tweets}
86+
viewabilityConfig={viewabilityConfig}
87+
/>
88+
</BottomSheet>
89+
</View>
90+
);
91+
//#endregion
92+
};
93+
94+
const Divider = () => {
95+
return <View style={styles.divider} />;
96+
};
97+
98+
const Footer = ({ isLoading }: { isLoading: boolean }) => {
99+
return (
100+
<View style={styles.footer}>
101+
{isLoading ? (
102+
<ActivityIndicator />
103+
) : (
104+
<Text style={styles.footerTitle}>No more tweets</Text>
105+
)}
106+
</View>
107+
);
108+
};
109+
110+
const Empty = () => {
111+
const title = 'Welcome to your timeline';
112+
const subTitle =
113+
"It's empty now but it won't be for long. Start following peopled you'll see Tweets show up here";
114+
return (
115+
<View style={styles.emptyComponent} testID="EmptyComponent">
116+
<Text style={styles.emptyComponentTitle}>{title}</Text>
117+
<Text style={styles.emptyComponentSubtitle}>{subTitle}</Text>
118+
</View>
119+
);
120+
};
121+
122+
const styles = StyleSheet.create({
123+
container: {
124+
flex: 1,
125+
padding: 24,
126+
},
127+
128+
divider: {
129+
width: '100%',
130+
height: StyleSheet.hairlineWidth,
131+
backgroundColor: '#DDD',
132+
},
133+
header: {
134+
height: 40,
135+
justifyContent: 'center',
136+
alignItems: 'center',
137+
backgroundColor: '#1DA1F2',
138+
},
139+
footer: {
140+
height: 40,
141+
justifyContent: 'center',
142+
alignItems: 'center',
143+
},
144+
headerTitle: {
145+
color: '#FFFFFF',
146+
padding: 8,
147+
borderRadius: 12,
148+
fontSize: 12,
149+
},
150+
footerTitle: {
151+
padding: 8,
152+
borderRadius: 12,
153+
fontSize: 12,
154+
},
155+
emptyComponentTitle: {
156+
color: 'black',
157+
fontSize: 20,
158+
fontWeight: 'bold',
159+
},
160+
emptyComponentSubtitle: {
161+
color: '#808080',
162+
padding: 8,
163+
fontSize: 14,
164+
textAlign: 'center',
165+
},
166+
emptyComponent: {
167+
justifyContent: 'center',
168+
alignItems: 'center',
169+
flex: 1,
170+
},
171+
});
172+
173+
export default FlashListExample;

0 commit comments

Comments
 (0)