'react native flatlist animation is not working as expected on RTL devices
I'm working in app that needs flatlist animation with RTL devices. I built a small app to understand how it will work. When using the app with LTR devices, it works just fine, and when I use I18nManager.forceRTL(true) everything messed up.
I have three slides with two components, the first component shows the slide itself and the second shows the number of the slide.
When apply force RTL the first component starts with the first slide but the second component start form the last one!
Also, when I use two animations (transform and opacity) for one element of the first component. The first slide and the last slide disappeared!
Here is my code with force RTL:
import React, { useRef } from "react";
import {
SafeAreaView,
View,
StyleSheet,
Text,
StatusBar,
Dimensions,
Animated,
} from "react-native";
import { I18nManager } from "react-native";
I18nManager.allowRTL(true);
I18nManager.forceRTL(true);
const { width, height } = Dimensions.get("window");
const DATA = [
{
id: "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba",
title: "First Slide",
number: "1",
},
{
id: "3ac68afc-c605-48d3-a4f8-fbd91aa97f63",
title: "Second Slide",
number: "2",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d72",
title: "Third Slide",
number: "3",
},
];
const Item = ({ title, index, scrollX }) => {
const inputRange = [(index - 1) * width, index * width, (index + 1) * width];
const opacityInputRange = [
(index - 0.4) * width,
index * width,
(index + 0.4) * width,
];
const scale = scrollX.interpolate({
inputRange,
outputRange: [0, 1, 0],
});
const opacity = scrollX.interpolate({
inputRange: opacityInputRange,
outputRange: [0, 1, 0],
});
return (
<View style={styles.mainConatainer}>
<Animated.View
style={{
backgroundColor: "blue",
width: width / 1.6,
height: height / 3,
alignItems: "center",
justifyContent: "center",
borderRadius: width,
transform: [{ scale }],
opacity,
}}
>
<Animated.Text
style={{
fontSize: 32,
color: "white",
transform: [{ scale }],
}}
>
{title}
</Animated.Text>
</Animated.View>
</View>
);
};
const Footer = ({ scrollX }) => {
return (
<View>
{DATA.map(({ number }, index) => {
const inputRange = [
(index - 1) * width,
index * width,
(index + 1) * width,
];
const scale = scrollX.interpolate({
inputRange,
outputRange: [0, 1, 0],
});
return (
<Animated.View key={index} style={{ transform: [{ scale }] }}>
<Text
style={{
fontSize: 50,
alignItems: "center",
marginBottom: 50,
position: "absolute",
bottom: "40%",
right: "50%",
color: "blue",
}}
>
{number}
</Text>
</Animated.View>
);
})}
</View>
);
};
export default function App() {
const scrollX = useRef(new Animated.Value(0)).current;
return (
<SafeAreaView style={styles.container}>
<Animated.FlatList
data={DATA}
renderItem={({ item, index }) => (
<Item {...item} index={index} scrollX={scrollX} />
)}
keyExtractor={(item) => item.id}
horizontal
pagingEnabled
showsHorizontalScrollIndicator={false}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { x: scrollX } } }],
{ useNativeDriver: true }
)}
scrollEventThrottle={16}
inverted={true}
/>
<Footer scrollX={scrollX} />
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
backgroundColor: "lightgrey",
},
mainConatainer: {
width,
alignItems: "center",
justifyContent: "center",
},
item: {
backgroundColor: "blue",
width: width / 1.6,
height: height / 3,
alignItems: "center",
justifyContent: "center",
borderRadius: width,
},
title: {
fontSize: 64,
color: "white",
},
});
Without force RTL:
import React, { useRef } from "react";
import {
SafeAreaView,
View,
StyleSheet,
Text,
StatusBar,
Dimensions,
Animated,
} from "react-native";
const { width, height } = Dimensions.get("window");
const DATA = [
{
id: "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba",
title: "First Slide",
number: "1",
},
{
id: "3ac68afc-c605-48d3-a4f8-fbd91aa97f63",
title: "Second Slide",
number: "2",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d72",
title: "Third Slide",
number: "3",
},
];
const Item = ({ title, index, scrollX }) => {
const inputRange = [(index - 1) * width, index * width, (index + 1) * width];
const opacityInputRange = [
(index - 0.4) * width,
index * width,
(index + 0.4) * width,
];
const scale = scrollX.interpolate({
inputRange,
outputRange: [0, 1, 0],
});
const opacity = scrollX.interpolate({
inputRange: opacityInputRange,
outputRange: [0, 1, 0],
});
return (
<View style={styles.mainConatainer}>
<Animated.View
style={{
backgroundColor: "blue",
width: width / 1.6,
height: height / 3,
alignItems: "center",
justifyContent: "center",
borderRadius: width,
transform: [{ scale }],
opacity,
}}
>
<Animated.Text
style={{
fontSize: 32,
color: "white",
transform: [{ scale }],
}}
>
{title}
</Animated.Text>
</Animated.View>
</View>
);
};
const Footer = ({ scrollX }) => {
return (
<View>
{DATA.map(({ number }, index) => {
const inputRange = [
(index - 1) * width,
index * width,
(index + 1) * width,
];
const scale = scrollX.interpolate({
inputRange,
outputRange: [0, 1, 0],
});
return (
<Animated.View key={index} style={{ transform: [{ scale }] }}>
<Text
style={{
fontSize: 50,
alignItems: "center",
marginBottom: 50,
position: "absolute",
bottom: "40%",
right: "50%",
color: "blue",
}}
>
{number}
</Text>
</Animated.View>
);
})}
</View>
);
};
export default function App() {
const scrollX = useRef(new Animated.Value(0)).current;
return (
<SafeAreaView style={styles.container}>
<Animated.FlatList
data={DATA}
renderItem={({ item, index }) => (
<Item {...item} index={index} scrollX={scrollX} />
)}
keyExtractor={(item) => item.id}
horizontal
pagingEnabled
showsHorizontalScrollIndicator={false}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { x: scrollX } } }],
{ useNativeDriver: true }
)}
scrollEventThrottle={16}
inverted={true}
/>
<Footer scrollX={scrollX} />
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
backgroundColor: "lightgrey",
},
mainConatainer: {
width,
alignItems: "center",
justifyContent: "center",
},
item: {
backgroundColor: "blue",
width: width / 1.6,
height: height / 3,
alignItems: "center",
justifyContent: "center",
borderRadius: width,
},
title: {
fontSize: 64,
color: "white",
},
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|