// components/Dropdown.tsximport React from 'react';import { View, StyleSheet } from 'react-native';type DropdownItemProps = {// Define your properties here};type DropdownProps = {header: DropdownItemProps;options: DropdownItemProps[];};const Dropdown: React.FC<DropdownProps> = ({ header, options }) => {const dropdownItems = [header, ...options];const dropdownItemHeight = 80;const styles = StyleSheet.create({dropdownItem: {width: '95%',height: dropdownItemHeight,backgroundColor: '#1B1B1B', // Replace with your desired background color// Add other styles as needed},});return (<View>{dropdownItems.map((item, index) => (<View key={index} style={styles.dropdownItem}>{/* Content for each item */}</View>))}</View>);};export default Dropdown;
const dropdownItems = [header, ...options];const dropdownItemHeight = 80;const styles = StyleSheet.create({dropdownItem: {width: '95%',height: dropdownItemHeight,backgroundColor: '#1B1B1B', // Replace with your desired background color// Add other styles as needed},});return (<View>{dropdownItems.map((item, index) => (<View key={index} style={styles.dropdownItem}>{/* Content for each item */}</View>))}</View>);
// Inside the DropdownListItem componentconst DropdownListItem: React.FC<DropdownItemProps & { index: number }> = ({label,icon,index,}) => {// Other properties and styles remain the sameconst styles = StyleSheet.create({dropdownItem: {width: '95%',height: dropdownItemHeight,backgroundColor: '#1B1B1B', // Replace with your desired background colormarginTop: 10, // Add margin for spacingposition: 'absolute',top: index * (dropdownItemHeight + 10), // Adjust the top property based on the index// Add other styles as needed},});// Rest of the component};
// Inside the Dropdown componentconst Dropdown: React.FC<DropdownProps> = ({ header, options }) => {const dropdownItems = [header, ...options];const dropdownItemHeight = 80;// Calculate the total number of items in the dropdownconst dropdownItemsCount = dropdownItems.length;return (<View>{dropdownItems.map((item, index) => (<DropdownListItemkey={index}label={item.label}icon={item.icon}index={index}dropdownItemsCount={dropdownItemsCount}/>))}</View>);};
// Inside the DropdownListItem componentconst DropdownListItem: React.FC<DropdownItemProps & {index: number;dropdownItemsCount: number;}> = ({ label, icon, index, dropdownItemsCount }) => {// Calculate the total height of the dropdownconst fullDropdownHeight = dropdownItemsCount * (dropdownItemHeight + 10); // Including margin// Other properties and styles remain the sameconst styles = StyleSheet.create({dropdownItem: {width: '95%',height: dropdownItemHeight,backgroundColor: '#1B1B1B', // Replace with your desired background colormarginTop: 10, // Add margin for spacingposition: 'absolute',top: index * (dropdownItemHeight + 10), // Adjust the top property based on the indextransform: [{translateY: -fullDropdownHeight / 2, // Offset each item by half of the dropdown height},],// Add other styles as needed},});// Rest of the component};
// Inside the DropdownListItem componentconst DropdownListItem: React.FC<DropdownItemProps & { index: number }> = ({label,icon,index,}) => {// Other properties and styles remain the same// Define the collapsed and expanded top valuesconst collapsedTop = fullDropdownHeight / 2 - dropdownItemHeight; // Centered when collapsedconst expandedTop = index * (dropdownItemHeight + 10);// Rest of the component};
// Inside the DropdownListItem componentimport Animated, { useAnimatedStyle } from 'react-native-reanimated';const DropdownListItem: React.FC<DropdownItemProps & { index: number }> = ({label,icon,index,}) => {// Other properties and styles remain the same// Define the collapsed and expanded top valuesconst collapsedTop = fullDropdownHeight / 2 - dropdownItemHeight; // Centered when collapsedconst expandedTop = index * (dropdownItemHeight + 10);// Create the Reanimated styleconst itemStyle = useAnimatedStyle(() => {return {top: expandedTop, // Initially set to expandedTop};});// Rest of the component};
// Inside the Dropdown componentimport { useSharedValue } from 'react-native-reanimated';const Dropdown: React.FC<DropdownProps> = ({ header, options }) => {// ...// Define the state variable for dropdown stateconst isExpanded = useSharedValue(false);return (<View>{dropdownItems.map((item, index) => (<DropdownListItemkey={index}label={item.label}icon={item.icon}index={index}isExpanded={isExpanded}/>))}</View>);};
// Inside the DropdownListItem componentconst DropdownListItem: React.FC<DropdownItemProps & {index: number;isExpanded: Animated.SharedValue<boolean>;}> = ({ label, icon, index, isExpanded }) => {// ...// Update the itemStyle to set top based on isExpandedconst itemStyle = useAnimatedStyle(() => {return {top: isExpanded.value ? expandedTop : collapsedTop,};});// ...return (<Animated.View style={[styles.dropdownItem, itemStyle]}>{/* Content for each item */}</Animated.View>);};
// Inside the DropdownListItem componentconst DropdownListItem: React.FC<DropdownItemProps & {index: number;isExpanded: Animated.SharedValue<boolean>;}> = ({ label, icon, index, isExpanded }) => {// ...// Toggle the isExpanded value on pressconst toggleDropdown = () => {isExpanded.value = !isExpanded.value;};return (<Animated.Viewstyle={[styles.dropdownItem, itemStyle]}onTouchStart={toggleDropdown}>{/* Content for each item */}</Animated.View>);};
// Inside the Dropdown componentimport { withSpring } from 'react-native-reanimated';const Dropdown: React.FC<DropdownProps> = ({ header, options }) => {// ...// Add animation to the top propertyconst itemStyle = useAnimatedStyle(() => {return {top: withSpring(isExpanded.value ? expandedTop : collapsedTop),};});// ...return (<View>{dropdownItems.map((item, index) => (<DropdownListItemkey={index}label={item.label}icon={item.icon}index={index}isExpanded={isExpanded}/>))}</View>);};
// Inside the DropdownListItem componentconst DropdownListItem: React.FC<DropdownItemProps & {index: number;isExpanded: Animated.SharedValue<boolean>;}> = ({ label, icon, index, isExpanded }) => {// Check if this item is the headerconst isHeader = index === 0;// Toggle the isExpanded value on press only if this item is the headerconst toggleDropdown = () => {if (isHeader) {isExpanded.value = !isExpanded.value;}};return (<Animated.Viewstyle={[styles.dropdownItem, itemStyle]}onTouchStart={toggleDropdown}>{/* Content for each item */}</Animated.View>);};
// Inside the DropdownListItem componentconst DropdownListItem: React.FC<DropdownItemProps & {index: number;isExpanded: Animated.SharedValue<boolean>;}> = ({ label, icon, index, isExpanded }) => {// Determine zIndex based on the item's positionconst zIndex = dropdownItemsCount - index;// Update the itemStyle to set top, zIndex, and scale based on isExpandedconst itemStyle = useAnimatedStyle(() => {return {top: withSpring(isExpanded.value ? expandedTop : collapsedTop),zIndex: zIndex, // Header always on top};});return (<Animated.Viewstyle={[styles.dropdownItem, itemStyle]}onTouchStart={toggleDropdown}>{/* Content for each item */}</Animated.View>);};
// Inside the DropdownListItem componentconst DropdownListItem: React.FC<DropdownItemProps & {index: number;isExpanded: Animated.SharedValue<boolean>;}> = ({ label, icon, index, isExpanded }) => {// Determine zIndex based on the item's positionconst zIndex = dropdownItemsCount - index;// Define the scale values for expanded and collapsed statesconst expandedScale = 1; // No scaling when expandedconst collapsedScale = 1 - index * 0.08; // Slightly scale down items in the collapsed state// Update the itemStyle to set top, zIndex, and scale based on isExpandedconst itemStyle = useAnimatedStyle(() => {return {top: isExpanded.value ? expandedTop : collapsedTop,zIndex: isHeader ? dropdownItemsCount + 1 : zIndex, // Header always on toptransform: [{ scale: isExpanded.value ? expandedScale : collapsedScale }, // Adjust scale{translateY: fullDropdownHeight / 2,},],};});return (<Animated.Viewstyle={[styles.dropdownItem, itemStyle]}onTouchStart={toggleDropdown}>{/* Content for each item */}</Animated.View>);};
// Inside the DropdownListItem componentconst DropdownListItem: React.FC<DropdownItemProps & {index: number;isExpanded: Animated.SharedValue<boolean>;}> = ({ label, icon, index, isExpanded }) => {// ...// Add animation to the scale propertyconst itemStyle = useAnimatedStyle(() => {return {top: withSpring(isExpanded.value ? expandedTop : collapsedTop),zIndex: zIndex,transform: [{scale: withSpring(isExpanded.value ? expandedScale : collapsedScale),}, // Adjust scale with spring transition{translateY: fullDropdownHeight / 2,},],};});// ...return (<Animated.Viewstyle={[styles.dropdownItem, itemStyle]}onTouchStart={toggleDropdown}>{/* Content for each item */}</Animated.View>);};
import color from 'color';
// Assuming 'colorToAnimate' is your color variableconst expandedBackgroundColor = '#1B1B1B';const collapsedBackgroundColor = Color(expandedBackgroundColor).lighten(index * 0.25).hex();// Inside the DropdownListItem componentconst rStyle = useAnimatedStyle(() => {return {backgroundColor: withTiming(isExpanded.value ? expandedBackgroundColor : collapsedBackgroundColor),top: withSpring(isExpanded.value ? expandedTop : collapsedTop),transform: [{scale: withSpring(isExpanded.value ? expandedScale : collapsedScale),},{translateY: fullDropdownHeight / 2,},],};}, []);
import { StyleSheet } from 'react-native';const styles = StyleSheet.create({container: {flex: 1,flexDirection: 'row',alignItems: 'center',justifyContent: 'space-between',},label: {color: 'white',fontSize: 16,textTransform: 'uppercase',letterSpacing: 1,},iconContainer: {width: 50,aspectRatio: 1,backgroundColor: 'black',borderRadius: 5,justifyContent: 'center',alignItems: 'center',position: 'absolute',},});
// Inside your componentimport { View, Text } from 'react-native';const DropdownHeader = () => {// ...return (<View style={styles.container}><Text style={styles.label}>Your Label</Text><View style={styles.iconContainer}>{/* Left Icon */}<MaterialIcons name="arrow-forward" size={24} color="white" /></View><View style={[styles.iconContainer, { right: 15 }]}>{/* Right Icon */}{isHeader ? (<AntDesign name="arrowforward" size={24} color="white" />) : (<Ionicons name="ios-arrow-forward" size={24} color="white" />)}</View></View>);};
import { View } from 'react-native';import { AntDesign } from '@expo/vector-icons';import Animated, {useSharedValue,useAnimatedStyle,withSpring,} from 'react-native-reanimated';
// Inside the Dropdown ListItem componentconst rHeaderArrowIconStyle = useAnimatedStyle(() => {return {transform: [{rotate: withTiming(isHeader && isExpanded.value ? '90deg' : '0deg'),},],};});return (// ...<Animated.Viewstyle={[styles.iconContainer,rHeaderArrowIconStyle,{right: 15,backgroundColor: 'transparent',},]}><MaterialIconsname={isHeader ? 'arrow-forward-ios' : 'arrow-forward'}size={25}color={'#D4D4D4'}/></Animated.View>);
import { View } from 'react-native';import { AntDesign } from '@expo/vector-icons';import Animated, {useSharedValue,useAnimatedStyle,withSpring,} from 'react-native-reanimated';
// Inside the Dropdown ListItem componentconst rLeftIconOpacityStyle = useAnimatedStyle(() => {return {opacity: withTiming(isHeader ? 1 : isExpanded.value ? 1 : 0),};}, [isHeader]);return (//<View style={styles.container}><Animated.Viewstyle={[styles.iconContainer,{left: 15,},rLeftIconOpacityStyle,]}><AntDesign name={iconName as any} size={25} color="#D4D4D4" /></Animated.View><Text style={styles.label}>{label}</Text>// Arrow Icon Component ...</View>);
Every week I send out a newsletter sharing new things about React Native animations.