Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scrollTo examples for FlatList and FlashList #6207

Merged
merged 12 commits into from
Jul 9, 2024
Merged
1 change: 1 addition & 0 deletions apps/common-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@react-navigation/native": "^6.1.9",
"@react-navigation/native-stack": "^6.9.17",
"@react-navigation/stack": "^6.3.18",
"@shopify/flash-list": "^1.6.4",
MatiPl01 marked this conversation as resolved.
Show resolved Hide resolved
MatiPl01 marked this conversation as resolved.
Show resolved Hide resolved
"@stylexjs/babel-plugin": "^0.7.0",
"@tsconfig/react-native": "^3.0.0",
"@types/d3-shape": "^3.1.1",
Expand Down
12 changes: 8 additions & 4 deletions apps/common-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ function isFabric(): boolean {
return !!(global as Record<string, unknown>)._IS_FABRIC;
}

const noop = () => undefined;
MatiPl01 marked this conversation as resolved.
Show resolved Hide resolved

type RootStackParamList = { [P in keyof typeof EXAMPLES]: undefined } & {
Home: undefined;
};
Expand Down Expand Up @@ -200,7 +202,7 @@ export default function App() {
};

if (!isReady) {
restoreState();
restoreState().catch(noop);
MatiPl01 marked this conversation as resolved.
Show resolved Hide resolved
}
}, [isReady]);

Expand All @@ -219,9 +221,11 @@ export default function App() {
<NavigationContainer
linking={linking}
initialState={initialState}
onStateChange={(state) =>
AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state))
}>
onStateChange={(state) => {
AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state)).catch(
noop
);
}}>
MatiPl01 marked this conversation as resolved.
Show resolved Hide resolved
MatiPl01 marked this conversation as resolved.
Show resolved Hide resolved
<Stack.Navigator>
<Stack.Screen
name="Home"
Expand Down
81 changes: 81 additions & 0 deletions apps/common-app/src/examples/ScrollToFlashListExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import type { AnimatedProps } from 'react-native-reanimated';
MatiPl01 marked this conversation as resolved.
Show resolved Hide resolved
import Animated, {
runOnUI,
scrollTo,
useAnimatedRef,
} from 'react-native-reanimated';
import { Button, StyleSheet, Switch, Text, View } from 'react-native';
import type { FlashListProps, ListRenderItem } from '@shopify/flash-list';
import { FlashList } from '@shopify/flash-list';

import React, { useCallback } from 'react';

const AnimatedFlashList = Animated.createAnimatedComponent(
FlashList
) as React.ComponentClass<AnimatedProps<FlashListProps<number>>>;

export default function ScrollToFlashListExample() {
const [animated, setAnimated] = React.useState(true);

const aref = useAnimatedRef<Animated.FlatList<number>>();

const scrollFromJS = () => {
console.log(_WORKLET);
aref.current?.scrollToOffset({ offset: Math.random() * 2000, animated });
};

const scrollFromUI = () => {
runOnUI(() => {
console.log(_WORKLET);
scrollTo(aref, 0, Math.random() * 2000, animated);
})();
};

const renderItem = useCallback<ListRenderItem<number>>(
({ item }) => (
<View style={styles.cell}>
<Text style={styles.text}>{item}</Text>
</View>
),
[]
);

return (
<>
<View style={styles.buttons}>
<Switch
value={animated}
onValueChange={setAnimated}
style={styles.switch}
/>
<Button onPress={scrollFromJS} title="Scroll from JS" />
<Button onPress={scrollFromUI} title="Scroll from UI" />
</View>
<AnimatedFlashList
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ref={aref as any}
estimatedItemSize={60}
renderItem={renderItem}
data={[...Array(100).keys()]}
/>
</>
);
}

const styles = StyleSheet.create({
switch: {
marginBottom: 10,
},
buttons: {
marginTop: 80,
marginBottom: 40,
alignItems: 'center',
},
cell: {
height: 60,
},
text: {
fontSize: 50,
textAlign: 'center',
},
});
71 changes: 71 additions & 0 deletions apps/common-app/src/examples/ScrollToFlatListExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import Animated, {
runOnUI,
scrollTo,
useAnimatedRef,
} from 'react-native-reanimated';
import type { ListRenderItem } from 'react-native';
import { Button, StyleSheet, Switch, Text, View } from 'react-native';

import React, { useCallback } from 'react';

export default function ScrollToFlatListExample() {
const [animated, setAnimated] = React.useState(true);

const aref = useAnimatedRef<Animated.FlatList<number>>();

const scrollFromJS = () => {
console.log(_WORKLET);
aref.current?.scrollToOffset({ offset: Math.random() * 2000, animated });
};

const scrollFromUI = () => {
runOnUI(() => {
console.log(_WORKLET);
scrollTo(aref, 0, Math.random() * 2000, animated);
})();
};

const renderItem = useCallback<ListRenderItem<number>>(
({ item }) => <Text style={styles.text}>{item}</Text>,
[]
);

return (
<>
<View style={styles.buttons}>
<Switch
value={animated}
onValueChange={setAnimated}
style={styles.switch}
/>
<Button onPress={scrollFromJS} title="Scroll from JS" />
<Button onPress={scrollFromUI} title="Scroll from UI" />
</View>
<Animated.FlatList
ref={aref}
renderItem={renderItem}
data={[...Array(100).keys()]}
style={styles.flatList}
/>
</>
);
}

const styles = StyleSheet.create({
switch: {
marginBottom: 10,
},
buttons: {
marginTop: 80,
marginBottom: 40,
alignItems: 'center',
},
flatList: {
flex: 1,
width: '100%',
},
text: {
fontSize: 50,
textAlign: 'center',
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Button, StyleSheet, Switch, Text, View } from 'react-native';

import React from 'react';

export default function ScrollToExample() {
export default function ScrollToScrollViewExample() {
const [animated, setAnimated] = React.useState(true);

const aref = useAnimatedRef<Animated.ScrollView>();
Expand Down
20 changes: 16 additions & 4 deletions apps/common-app/src/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ import TransitionRestartExample from './SharedElementTransitions/TransitionResta
import ScreenStackExample from './ScreenStackExample';
import ScreenStackHeaderConfigBackgroundColorExample from './ScreenStackHeaderConfigBackgroundColorExample';
import ScrollEventExample from './ScrollEventExample';
import ScrollToExample from './ScrollToExample';
import ScrollToScrollViewExample from './ScrollToScrollViewExample';
import ScrollToFlatListExample from './ScrollToFlatListExample';
import ScrollToFlashListExample from './ScrollToFlashListExample';
import ScrollViewExample from './ScrollViewExample';
import ScrollViewOffsetExample from './ScrollViewOffsetExample';
import ScrollableViewExample from './ScrollableViewExample';
Expand Down Expand Up @@ -354,10 +356,20 @@ export const EXAMPLES: Record<string, Example> = {
title: 'useAnimatedScrollHandler',
screen: ScrollViewExample,
},
ScrollToExample: {
ScrollToScrollViewExample: {
icon: '🦘',
title: 'scrollTo',
screen: ScrollToExample,
title: 'scrollTo - ScrollView',
screen: ScrollToScrollViewExample,
},
ScrollToFlatListExample: {
icon: '🦆',
title: 'scrollTo - FlatList',
screen: ScrollToFlatListExample,
},
ScrollToFlashListExample: {
icon: '⚡',
title: 'scrollTo - FlashList',
screen: ScrollToFlashListExample,
},
ScrollViewOffsetExample: {
icon: '𝌍',
Expand Down
1 change: 1 addition & 0 deletions apps/fabric-example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ dependencies {
}

implementation("androidx.core:core-splashscreen:1.0.1")
implementation project(':@shopify-flash-list')
MatiPl01 marked this conversation as resolved.
Show resolved Hide resolved
}

apply from: file("../../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.soloader.SoLoader
import com.shopify.reactnative.flash_list.ReactNativeFlashListPackage

class MainApplication : Application(), ReactApplication {

override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
add(ReactNativeFlashListPackage())
}

override fun getJSMainModuleName(): String = "index"
Expand Down
3 changes: 3 additions & 0 deletions apps/fabric-example/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ rootProject.name = 'FabricExample'
apply from: file("../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'
includeBuild('../../../node_modules/@react-native/gradle-plugin')

include ':@shopify-flash-list'
project(':@shopify-flash-list').projectDir = new File(rootProject.projectDir, '../../../node_modules/@shopify/flash-list/android')
MatiPl01 marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions apps/fabric-example/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ target 'FabricExample' do
# Pods for testing
end

pod 'RNFlashList', :path => '../../../node_modules/@shopify/flash-list'
MatiPl01 marked this conversation as resolved.
Show resolved Hide resolved

post_install do |installer|
# https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
react_native_post_install(
Expand Down
10 changes: 8 additions & 2 deletions apps/fabric-example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1337,6 +1337,8 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- RNFlashList (1.6.4):
- React-Core
- RNGestureHandler (2.16.2):
- DoubleConversion
- glog
Expand Down Expand Up @@ -1530,6 +1532,7 @@ DEPENDENCIES:
- "RNCAsyncStorage (from `../../../node_modules/@react-native-async-storage/async-storage`)"
- "RNCMaskedView (from `../../../node_modules/@react-native-masked-view/masked-view`)"
- "RNCPicker (from `../../../node_modules/@react-native-picker/picker`)"
- "RNFlashList (from `../../../node_modules/@shopify/flash-list`)"
- RNGestureHandler (from `../../../node_modules/react-native-gesture-handler`)
- RNReanimated (from `../../../node_modules/react-native-reanimated`)
- RNScreens (from `../../../node_modules/react-native-screens`)
Expand Down Expand Up @@ -1660,6 +1663,8 @@ EXTERNAL SOURCES:
:path: "../../../node_modules/@react-native-masked-view/masked-view"
RNCPicker:
:path: "../../../node_modules/@react-native-picker/picker"
RNFlashList:
:path: "../../../node_modules/@shopify/flash-list"
RNGestureHandler:
:path: "../../../node_modules/react-native-gesture-handler"
RNReanimated:
Expand Down Expand Up @@ -1731,13 +1736,14 @@ SPEC CHECKSUMS:
RNCAsyncStorage: f2add1326156dc313df59d855c11f459059e4ffd
RNCMaskedView: 090213d32d8b3bb83a4dcb7d12c18f0152591906
RNCPicker: e84f13a98cbc8977870692948ccae15a389461bb
RNFlashList: b521ebdd7f9352673817f1d98e8bdc0c8cf8545b
RNGestureHandler: 156548e18203327173a764c6932a3f52e90cb9cd
RNReanimated: 3a1b8b69bd5afffb33f65ed0d9f49f3c83bdaeb7
RNScreens: a68878603ae871d339f683bc683803545422986f
RNSVG: 02051bffb0b2fb2166e85009a58211643434ff63
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
Yoga: ff1d575b119f510a5de23c22a794872562078ccf
Yoga: 56f906bf6c11c931588191dde1229fd3e4e3d557

PODFILE CHECKSUM: baaf1c2684753f6bff7da2866e138e1af1fe3499
PODFILE CHECKSUM: 1bada524226eb6be27cd8615effcb1b9fef34d37

COCOAPODS: 1.14.3
3 changes: 2 additions & 1 deletion apps/paper-example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ react {
// The root of your project, i.e. where "package.json" lives. Default is '..'
// root = file("../")
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
reactNativeDir = file("../../../../node_modules/react-native")
reactNativeDir = file("../../../../node_modules/react-native")
// The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
codegenDir = file("../../../../node_modules/@react-native/codegen")
// The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
Expand Down Expand Up @@ -116,6 +116,7 @@ dependencies {
}

implementation("androidx.core:core-splashscreen:1.0.1")
implementation project(':@shopify-flash-list')
}

apply from: file("../../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.soloader.SoLoader
import com.shopify.reactnative.flash_list.ReactNativeFlashListPackage

class MainApplication : Application(), ReactApplication {

override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
add(ReactNativeFlashListPackage())
}

override fun getJSMainModuleName(): String = "index"
Expand Down
3 changes: 3 additions & 0 deletions apps/paper-example/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ rootProject.name = 'ReanimatedExample'
apply from: file("../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'
includeBuild('../../../node_modules/@react-native/gradle-plugin')

include ':@shopify-flash-list'
project(':@shopify-flash-list').projectDir = new File(rootProject.projectDir, '../../../node_modules/@shopify/flash-list/android')
2 changes: 2 additions & 0 deletions apps/paper-example/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ target 'ReanimatedExample' do
# Pods for testing
end

pod 'RNFlashList', :path => '../../../node_modules/@shopify/flash-list'

post_install do |installer|
# https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
react_native_post_install(
Expand Down
Loading
Loading