I would like to create an animated tab menu to learn react-native-reanimated. Here is my full code:
import { View, StyleSheet, Text, Pressable, Dimensions } from "react-native";
import Animated, {
useAnimatedStyle,
useSharedValue,
withSpring,
} from "react-native-reanimated";
const numberOfTabs = 3;
const windowWidth = Dimensions.get("window").width;
const tabWith = windowWidth / numberOfTabs;
export default function App() {
const position = useSharedValue(0);
const indexArray = [];
for (let index = 0; index < numberOfTabs; index++) {
indexArray.push(index);
}
const selectedButtonAnimatedStyle = useAnimatedStyle(() => {
return {
position: "absolute",
left: position.value,
right: 0,
top: 0,
width: tabWith,
};
});
const handlePress = (index) => {
position.value = withSpring(tabWith * index);
};
return (
<View style={styles.container}>
<View style={styles.buttonGroup}>
<Animated.View
style={[styles.selectedButton, selectedButtonAnimatedStyle]}
/>
{indexArray.map((i) => (
<Pressable
key={i}
style={styles.menuButton}
onPress={() => handlePress(i)}>
<Text>category {i}</Text>
</Pressable>
))}
</View>
</View>
);
}
const styles = StyleSheet.create({
menuButton: {
width: tabWith,
alignItems: "center",
justifyContent: "center",
},
selectedButton: {
backgroundColor: "green",
height: "100%",
},
buttonGroup: {
flex: 1,
width: "100%",
flexDirection: "row",
maxHeight: 50,
},
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
width: "100%",
//margin: 10, //add this to break
},
});
Expo snack to run and modify the code: https://snack.expo.dev/@tomwaitforitmy/tab-menu
Do I need position: "absolute"
and/or Dimensions.get("window").width
? The layout breaks if you wrap it with margin
or padding
. In other words: Is my index-based positioning required? I would prefer to have relative alignments to use the menu wherever I want. However, I am fairly unexperienced with react-native-reanimated and I don't know if that's a decent idea or just foolish wish. Thank you for any feedback!
PRO TIP: do not use
Dimensions.get("window").width;
If your user will change from portrait to landscape, open his foldable or for some reason "change the dimension" of the screen, you will not react to that change. I needed to refactor all my app because of this :)
Move the logic inside your component and use:
const { width } = useWindowDimensions();
Then, you came up with a pretty smart idea. I think there are no other ways rather than using positioning, but you can provide some fixes for margins, if you define them togheter with all your stuff
MAYBE, and I repeat, MAYBE you can achieve something animating flex property, for example making three buttons of
flex: 1
and your "selection indicator" only for "just one flex, not all three". But this is only loud thinking.