Skip to content

Instantly share code, notes, and snippets.

@amandeepmittal
Created October 16, 2020 10:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amandeepmittal/b8f40a3bfed57646f2ad9ae6928fbb1b to your computer and use it in GitHub Desktop.
Save amandeepmittal/b8f40a3bfed57646f2ad9ae6928fbb1b to your computer and use it in GitHub Desktop.
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Image,
ScrollView
} from 'react-native';
import { AntDesign } from '@expo/vector-icons';
import { FlatList } from 'react-native-gesture-handler';
import { SafeAreaView } from 'react-native-safe-area-context';
import { SharedElement } from 'react-navigation-shared-element';
import data, { tabs, popularFood } from '../config/data/food';
import { SPACING, width, foodConfig } from '../config/theme';
export const CELL_WIDTH = width * 0.64;
const CELL_HEIGHT = CELL_WIDTH * 1.4;
const FULL_SIZE = CELL_WIDTH + SPACING * 2;
export default function FoodList({ navigation }) {
const [selectedTab, setSelectedTab] = useState(tabs[0]);
return (
<ScrollView showsVerticalScrollIndicator={false}>
<SafeAreaView style={{ flex: 1, paddingVertical: SPACING }}>
<FlatList
data={tabs}
keyExtractor={(item, index) => `${item}-${index}`}
horizontal
showsHorizontalScrollIndicator={false}
style={{ flexGrow: 0 }}
contentContainerStyle={{ padding: SPACING }}
renderItem={({ item: tab }) => {
return (
<TouchableOpacity onPress={() => setSelectedTab(tab)}>
<View
style={[
styles.pill,
{
backgroundColor:
selectedTab === tab
? foodConfig.colors.orange
: 'transparent'
}
]}
>
<Text
style={[
styles.pillText,
{
color: selectedTab === tab ? 'white' : '#000'
}
]}
>
{tab}
</Text>
</View>
</TouchableOpacity>
);
}}
/>
<FlatList
data={data}
keyExtractor={item => item.key}
horizontal
showsHorizontalScrollIndicator={false}
snapToInterval={FULL_SIZE}
decelerationRate='fast'
renderItem={({ item }) => {
return (
<TouchableOpacity
onPress={() => navigation.navigate('FoodListDetails', { item })}
style={{
width: CELL_WIDTH,
height: CELL_HEIGHT,
margin: SPACING
}}
>
<View
style={{
flex: 1,
padding: SPACING,
justifyContent: 'center'
}}
>
<SharedElement
id={`item.${item.key}.bg`}
style={[StyleSheet.absoluteFillObject]}
>
<View
style={[
StyleSheet.absoluteFillObject,
{ backgroundColor: item.color, borderRadius: 16 }
]}
/>
</SharedElement>
<SharedElement
id={`item.${item.key}.meta`}
style={[StyleSheet.absoluteFillObject]}
>
<View
style={{
position: 'absolute',
top: SPACING,
left: SPACING
}}
>
<Text style={styles.type}>{item.type}</Text>
<Text style={styles.subType}>{item.subType}</Text>
</View>
</SharedElement>
<SharedElement
id={`item.${item.key}.image`}
style={styles.image}
>
<Image source={{ uri: item.image }} style={styles.image} />
</SharedElement>
</View>
</TouchableOpacity>
);
}}
/>
<FlatList
data={popularFood}
keyExtractor={item => item.key}
showsVerticalScrollIndicator={false}
scrollEnabled={false}
renderItem={({ item }) => {
return (
<View
style={{
flexDirection: 'row',
alignItems: 'center',
padding: SPACING
}}
>
<Image
source={{ uri: item.image }}
style={styles.popularImage}
/>
<View style={{ flex: 1 }}>
<Text style={styles.popularType}>{item.type}</Text>
<View style={{ flexDirection: 'row' }}>
<AntDesign
name='star'
size={16}
color={foodConfig.colors.orange}
style={{ marginRight: SPACING / 2 }}
/>
<Text style={{ fontWeight: '700' }}>{item.rating}</Text>
</View>
</View>
<Text style={styles.popularPrice}>{item.price}</Text>
</View>
);
}}
/>
</SafeAreaView>
</ScrollView>
);
}
const styles = StyleSheet.create({
pill: {
paddingHorizontal: SPACING,
paddingVertical: SPACING / 2,
borderRadius: 12
},
pillText: {
fontWeight: '700'
},
popularType: {
fontWeight: '700',
fontSize: 16
},
popularImage: {
height: 54,
width: 53,
resizeMode: 'contain',
marginRight: SPACING
},
popularPrice: {
fontWeight: '800'
},
type: {
fontWeight: '800',
fontSize: 22
},
subType: {
fontSize: 12,
opacity: 0.8
},
image: {
width: CELL_WIDTH * 0.7,
height: CELL_WIDTH * 0.7,
alignSelf: 'center',
resizeMode: 'contain',
position: 'absolute'
}
});
import React from 'react';
import { View, StyleSheet, Text, Image } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { AntDesign } from '@expo/vector-icons';
import * as Animatable from 'react-native-animatable';
import { SharedElement } from 'react-navigation-shared-element';
import { SPACING, width, height } from '../config/theme';
import { CELL_WIDTH } from './FoodList';
const animation = {
0: { opacity: 0, translateY: 50 },
1: { opacity: 1, translateY: 0 }
};
const DURATION = 400;
const createAnimation = from => ({
0: { opacity: 0, translateY: -100, translateX: from },
1: { opacity: 1, translateY: 0, translateX: 0 }
});
const animations = [
createAnimation(width * 0.3),
createAnimation(0),
createAnimation(-width * 0.3)
];
const AnimatableAntDesign = Animatable.createAnimatableComponent(AntDesign);
const FoodListDetails = ({ navigation, route }) => {
const { item } = route.params;
return (
<SafeAreaView style={{ flex: 1 }}>
<AnimatableAntDesign
useNativeDriver
delay={DURATION}
animation='fadeIn'
name='close'
size={28}
style={{
padding: SPACING,
position: 'absolute',
top: SPACING + 18,
right: SPACING,
zIndex: 2
}}
color={'#333'}
onPress={() => {
navigation.goBack();
}}
/>
<SharedElement
id={`item.${item.key}.bg`}
style={[StyleSheet.absoluteFillObject]}
>
<View
style={[
StyleSheet.absoluteFillObject,
{ backgroundColor: item.color, borderRadius: 0 }
]}
/>
</SharedElement>
<SharedElement
id={`item.${item.key}.meta`}
style={[StyleSheet.absoluteFillObject]}
>
<View
style={{
position: 'absolute',
top: SPACING * 4,
left: SPACING * 2
}}
>
<Text style={styles.type} numberOfLines={1} adjustsFontSizeToFit>
{item.type}
</Text>
<Text style={styles.subType}>{item.subType}</Text>
</View>
</SharedElement>
<View style={{ marginTop: height * 0.12 }}>
<SharedElement id={`item.${item.key}.image`}>
<Image source={{ uri: item.image }} style={styles.image} />
</SharedElement>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-evenly',
marginBottom: SPACING * 3
}}
>
{item.subcategories.map((subCategory, index) => {
return (
<Animatable.View
useNativeDriver={true}
animation={animations[index]}
delay={DURATION}
key={subCategory.key}
style={{
padding: SPACING,
backgroundColor: `${item.fullColor}95`,
borderRadius: 50
}}
>
<Image
source={{ uri: subCategory.image }}
style={{ width: 32, height: 32, resizeMode: 'contain' }}
/>
</Animatable.View>
);
})}
</View>
</View>
<View style={{ padding: SPACING }}>
<Animatable.Text
useNativeDriver={true}
animation={animation}
delay={DURATION + 300}
style={{ fontSize: 32, fontWeight: '700', marginBottom: SPACING / 2 }}
>
{item.price}
</Animatable.Text>
<Animatable.Text
useNativeDriver={true}
animation={animation}
delay={DURATION + 400}
style={{ fontSize: 14, lineHeight: 20, color: 'rgba(0,0,0,0.7)' }}
>
{item.description}
</Animatable.Text>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
type: {
fontWeight: '800',
fontSize: 22
},
subType: {
fontSize: 14,
opacity: 0.8
},
image: {
width: CELL_WIDTH * 0.9,
height: CELL_WIDTH * 0.9,
alignSelf: 'center',
resizeMode: 'contain',
marginVertical: SPACING * 4,
zIndex: 2
}
});
FoodListDetails.sharedElements = (route, otherRoute, showing) => {
const { item } = route.params;
return [
{
id: `item.${item.key}.bg`
},
{
id: `item.${item.key}.meta`,
resize: 'clip'
},
{
id: `item.${item.key}.image`
}
];
};
export default FoodListDetails;
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import { createSharedElementStackNavigator } from 'react-navigation-shared-element';
import { Easing } from 'react-native';
import FoodList from '../../screens/FoodList';
import FoodListDetails from '../../screens/FoodListDetails';
const FoodStack = createSharedElementStackNavigator();
const options = {
gestureEnabled: false,
headerBackTitleVisible: false,
transitionSpec: {
open: {
animation: 'timing',
config: { duration: 400, easing: Easing.inOut(Easing.ease) }
},
close: {
animation: 'timing',
config: { duration: 400, easing: Easing.inOut(Easing.ease) }
}
},
cardStyleInterpolator: ({ current: { progress } }) => {
return {
cardStyle: {
opacity: progress
}
};
}
};
export default function FoodTransitionNavigator() {
return (
<FoodStack.Navigator headerMode='none'>
<FoodStack.Screen name='FoodList' component={FoodList} />
<FoodStack.Screen
name='FoodListDetails'
component={FoodListDetails}
options={() => options}
/>
</FoodStack.Navigator>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment