Created
February 20, 2022 08:52
-
-
Save rs6000/46556c5f985b79887571eadf7496eacf to your computer and use it in GitHub Desktop.
React Native UI Example - Food
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// App.js | |
import 'react-native-gesture-handler'; | |
import React from 'react'; | |
import { StatusBar } from 'react-native'; | |
import { NavigationContainer } from '@react-navigation/native'; | |
import { createNativeStackNavigator } from '@react-navigation/native-stack'; | |
import COLORS from './src/consts/colors'; | |
import DetailsScreen from './src/views/screens/DetailsScreen'; | |
import BottomNavigator from './src/views/navigation/BottomNavigator'; | |
import OnBoardScreen from './src/views/screens/OnBoardScreen'; | |
const Stack = createNativeStackNavigator(); | |
const App = () => { | |
return ( | |
<NavigationContainer> | |
<StatusBar backgroundColor={COLORS.white} barStyle="dark-content" /> | |
<Stack.Navigator screenOptions={{ headerShown: false }}> | |
<Stack.Screen name="BoardScreen" component={OnBoardScreen} /> | |
<Stack.Screen name="Home" component={BottomNavigator} /> | |
<Stack.Screen name="DetailsScreen" component={DetailsScreen} /> | |
</Stack.Navigator> | |
</NavigationContainer> | |
); | |
}; | |
export default App; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src\views\screens\CartScreen.js | |
import { StyleSheet, Text, View, SafeAreaView, ScrollView, Image, FlatList } from 'react-native' | |
import React from 'react' | |
import Icon from 'react-native-vector-icons/MaterialIcons'; | |
import COLORS from "../../consts/colors" | |
import { PrimaryButton } from '../components/Button'; | |
import foods from '../../consts/foods'; | |
const CartScreen = ({ navigation }) => { | |
// 購物車資料 | |
const CartCard = ({ item }) => { | |
return ( | |
<View style={styles.CartCard}> | |
{/* 圖片 */} | |
<Image source={item.image} | |
style={{ width: 80, height: 80 }} | |
/> | |
{/* 名稱跟價錢 */} | |
<View style={{ flex: 1, height: 100, marginLeft: 10, paddingVertical: 20 }}> | |
<Text style={{ fontSize: 16, fontWeight: "bold" }}>{item.name}</Text> | |
<Text style={{ fontSize: 13, color: COLORS.grey }}>{item.ingredients}</Text> | |
<Text style={{ fontSize: 18, fontWeight: "bold" }}>${item.price}</Text> | |
</View> | |
{/* 訂購數量跟按鈕 */} | |
<View style={{ marginLeft: 20, alignItems: "center" }}> | |
<Text style={{ fontSize: 18, fontWeight: "bold" }}>3</Text> | |
<View style={styles.ActionBtn}> | |
<Icon name="remove" size={25} color={COLORS.white} /> | |
<Icon name="add" size={25} color={COLORS.white} /> | |
</View> | |
</View> | |
</View> | |
) | |
} | |
return ( | |
<SafeAreaView style={{ flex: 1, backgroundColor: COLORS.white }}> | |
<View style={styles.Header}> | |
<Icon name="arrow-back-ios" size={28} onPress={navigation.goBack} /> | |
<Text style={{ fontSize: 20, fontWeight: "bold" }}>Cart</Text> | |
</View> | |
{/* 購物車清單 */} | |
<FlatList showsVerticalScrollIndicator={false} | |
contentContainerStyle={{ paddingBottom: 80 }} | |
data={foods} | |
renderItem={({ item }) => <CartCard item={item} />} | |
ListFooterComponentStyle={{ paddingHorizontal: 20, marginTop: 20 }} | |
ListFooterComponent={() => ( | |
<View> | |
<View style={{ flexDirection: "row", justifyContent: "space-between", marginVertical: 15 }}> | |
<Text style={{ fontSize: 18, fontWeight: "bold" }}>Total Price</Text> | |
<Text style={{ fontSize: 18, fontWeight: "bold" }}>$50</Text> | |
</View> | |
<View style={{ marginTop: 30 }}> | |
<PrimaryButton title="CHECKOUT" /> | |
</View> | |
</View> | |
)} | |
/> | |
</SafeAreaView > | |
) | |
} | |
{/* <PrimaryButton title="Get Started" | |
onPress={() => navigation.navigate("Home")} | |
/> */} | |
const styles = StyleSheet.create({ | |
Header: { | |
flexDirection: "row", | |
paddingHorizontal: 20, | |
alignItems: "center", | |
marginHorizontal: 20, | |
}, | |
CartCard: { | |
flexDirection: "row", | |
height: 100, | |
elevation: 15, | |
borderRadius: 10, | |
backgroundColor: COLORS.white, | |
marginVertical: 10, | |
marginHorizontal: 20, | |
paddingHorizontal: 10, | |
alignItems: "center", | |
}, | |
ActionBtn: { | |
flexDirection: "row", | |
width: 80, | |
height: 30, | |
backgroundColor: COLORS.primary, | |
borderRadius: 30, | |
paddingHorizontal: 5, | |
justifyContent: "space-between", | |
alignItems: "center" | |
}, | |
}) | |
export default CartScreen |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src\consts\categories.js | |
const categories = [ | |
{id: '1', name: 'pizza', image: require('../assets/catergories/pizza.png')}, | |
{id: '2', name: 'Burger', image: require('../assets/catergories/burger.png')}, | |
{id: '3', name: 'Sushi', image: require('../assets/catergories/sushi.png')}, | |
{id: '4', name: 'Salad', image: require('../assets/catergories/salad.png')}, | |
]; | |
export default categories; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src\consts\colors.js | |
const COLORS = { | |
white: '#FFF', | |
dark: '#000', | |
primary: '#F9813A', | |
secondary: '#fedac5', | |
light: '#E5E5E5', | |
grey: '#908e8c', | |
}; | |
export default COLORS; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src\views\screens\DetailsScreen.js | |
import { StyleSheet, Text, View, SafeAreaView, ScrollView, Image } from 'react-native' | |
import React from 'react' | |
import Icon from 'react-native-vector-icons/MaterialIcons'; | |
import COLORS from "../../consts/colors" | |
import { OrderButton } from '../components/Button'; | |
const DetailsScreen = ({ navigation, route }) => { | |
// 設定 item變數 來接收 上一頁傳來的參數 | |
const item = route.params; | |
// 印出 上一頁傳來的參數 測試用 | |
// console.log(item) | |
return ( | |
<SafeAreaView style={{ backgroundColor: COLORS.white }}> | |
<View style={styles.Header}> | |
<Icon name="arrow-back-ios" size={28} onPress={() => navigation.goBack()} /> | |
<Text style={{ fontSize: 20, fontWeight: "bold" }}>Details</Text> | |
</View> | |
<ScrollView showsVerticalScrollIndicator={false}> | |
{/* 食物大圖 */} | |
<View style={{ justifyContent: "center", alignItems: "center", height: 280 }}> | |
<Image source={item.image} style={{ width: 220, height: 220, }} /> | |
</View> | |
{/* 食物簡介 */} | |
<View style={styles.Details}> | |
{/* 標題 */} | |
<View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center" }}> | |
<Text style={{ fontSize: 20, fontWeight: "bold", color: COLORS.white }}>{item.name}</Text> | |
<View style={styles.IconContainer}> | |
<Icon name="favorite-border" size={24} style={{ color: COLORS.primary }} /> | |
</View> | |
</View> | |
{/* 文字區塊 */} | |
<Text style={styles.DetailsText}>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Veritatis id labore architecto eius necessitatibus hic beatae, esse quia provident doloribus. Minima nesciunt a sint eaque dolor illo blanditiis aut corporis.</Text> | |
{/* 訂購按鈕 */} | |
<View style={{ marginTop: 40, marginBottom: 40 }}> | |
<OrderButton title="Add to Cart" /> | |
</View> | |
</View> | |
</ScrollView> | |
</SafeAreaView > | |
) | |
} | |
export default DetailsScreen | |
const styles = StyleSheet.create({ | |
Header: { | |
flexDirection: "row", | |
paddingHorizontal: 20, | |
alignItems: "center", | |
marginHorizontal: 20, | |
}, | |
Details: { | |
paddingHorizontal: 20, | |
paddingTop: 40, | |
paddingBottom: 60, | |
backgroundColor: COLORS.primary, | |
borderTopLeftRadius: 40, | |
borderTopRightRadius: 40, | |
}, | |
IconContainer: { | |
width: 50, | |
height: 50, | |
borderRadius: 25, | |
backgroundColor: COLORS.white, | |
justifyContent: "center", | |
alignItems: "center" | |
}, | |
DetailsText: { | |
marginTop: 10, | |
lineHeight: 22, | |
fontSize: 16, | |
color: COLORS.white | |
} | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src\consts\foods.js | |
const foods = [ | |
{ | |
id: '1', | |
name: 'Meat Pizza', | |
ingredients: 'Mixed Pizza', | |
price: '8.30', | |
image: require('../assets/meatPizza.png'), | |
}, | |
{ | |
id: '2', | |
name: 'Cheese Pizza', | |
ingredients: 'Cheese Pizza', | |
price: '7.10', | |
image: require('../assets/cheesePizza.png'), | |
}, | |
{ | |
id: '3', | |
name: 'Chicken Burger', | |
ingredients: 'Fried Chicken', | |
price: '5.10', | |
image: require('../assets/chickenBurger.png'), | |
}, | |
{ | |
id: '4', | |
name: 'Sushi Makizushi', | |
ingredients: 'Salmon Meat', | |
price: '9.55', | |
image: require('../assets/sushiMakizushi.png'), | |
}, | |
]; | |
export default foods; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src\views\screens\HomeScreen.js | |
import { SafeAreaView, StyleSheet, Text, View, Image, TextInput, ScrollView, TouchableOpacity, FlatList, Dimensions } from 'react-native' | |
import React, { useState } from 'react' | |
import Icon from 'react-native-vector-icons/MaterialIcons'; | |
import COLORS from "../../consts/colors" | |
import categories from '../../consts/categories' | |
import foods from '../../consts/foods'; | |
const { width } = Dimensions.get("screen") | |
const cardWidth = width / 2 - 20 | |
const HomeScreen = ({ navigation }) => { | |
const [selectedCategoryIndex, setSelectedCategoryIndex] = useState(0) | |
// 項目清單 | |
const ListCategories = () => { | |
return ( | |
<ScrollView | |
horizontal | |
showsHorizontalScrollIndicator={false} | |
contentContainerStyle={styles.CategoriesListContainer} | |
> | |
{/* 注意這邊的寫法 常用 但都記不住 XD */} | |
{categories.map((category, index) => ( | |
<TouchableOpacity key={index} | |
activeOpacity={0.8} | |
// 點選後 給index的值 (與下面的判斷式配合使用) | |
onPress={() => setSelectedCategoryIndex(index)} | |
> | |
<View style={{ | |
// 選取中的分類 與未選中的 背景色 判斷式 | |
backgroundColor: selectedCategoryIndex == index | |
? COLORS.primary | |
: COLORS.secondary | |
, ...styles.CategoryBtn | |
}}> | |
<View style={styles.CategoryBtnImg}> | |
<Image source={category.image} | |
style={{ width: 35, height: 35, resizeMode: "cover" }} | |
/> | |
</View> | |
{/* 被選中的顏色改變 判斷式 */} | |
<Text style={{ | |
fontSize: 15, fontWeight: "bold", marginLeft: 10, | |
color: selectedCategoryIndex == index | |
? COLORS.white | |
: COLORS.primary | |
}}>{category.name}</Text> | |
</View> | |
</TouchableOpacity> | |
)) | |
} | |
</ScrollView > | |
) | |
} | |
// 食物的卡片 | |
const Card = ({ food }) => { | |
return ( | |
// navigation.navigate 這邊傳遞的 DetailsScreen 要先在App.js裡面設定 | |
<TouchableOpacity underlayColor={COLORS.white} activeOpacity={0.9} onPress={() => navigation.navigate("DetailsScreen", food)}> | |
<View style={styles.Card}> | |
<View style={{ alignItems: "center", top: -40 }}> | |
<Image source={food.image} | |
style={{ | |
width: 120, | |
height: 120, | |
}} | |
/> | |
</View> | |
<View style={{ marginHorizontal: 20 }}> | |
<Text style={{ fontSize: 17, fontWeight: "bold" }}>{food.name}</Text> | |
<Text style={{ fontSize: 14, color: COLORS.grey, marginTop: 2 }}>{food.ingredients}</Text> | |
</View> | |
<View style={{ flexDirection: "row", marginTop: 10, marginHorizontal: 20, justifyContent: "space-between" }}> | |
<Text style={{ fontSize: 18, fontWeight: "bold" }}>${food.price}</Text> | |
<View style={styles.AddToCartBtn}> | |
<Icon name="add" size={20} color={COLORS.white} /> | |
</View> | |
</View> | |
</View> | |
</TouchableOpacity> | |
) | |
} | |
return ( | |
<SafeAreaView style={{ flex: 1, backgroundColor: COLORS.white }}> | |
<View style={styles.Header}> | |
<View> | |
<View style={{ flexDirection: "row" }}> | |
<Text style={{ fontSize: 28, }}>Hello,</Text> | |
<Text style={{ fontSize: 28, fontWeight: "bold", marginLeft: 10 }}>Hsu</Text> | |
</View> | |
<Text style={{ marginTop: 5, fontSize: 22, color: COLORS.grey }}>What do ypu want today?</Text> | |
</View> | |
{/* 頭像 */} | |
<Image source={require("../../assets/person.png")} | |
style={{ width: 50, height: 50, borderRadius: 25 }} | |
/> | |
</View> | |
{/* 搜尋列 */} | |
<View style={{ marginTop: 40, flexDirection: "row", paddingHorizontal: 20 }}> | |
<View style={styles.InputContainer}> | |
<Icon name="search" size={28} /> | |
<TextInput | |
placeholder='請輸入餐點名稱' | |
style={{ flex: 1, fontSize: 18, marginLeft: 5 }} | |
/> | |
</View> | |
{/* 排序按鈕 純展示 無作用 */} | |
<View style={styles.SortBtn}> | |
<Icon name="tune" size={28} color={COLORS.white} /> | |
</View> | |
</View> | |
{/* 菜單項目的分類欄 */} | |
<View> | |
<ListCategories /> | |
</View> | |
{/* 卡片 */} | |
<FlatList | |
showsVerticalScrollIndicator={false} | |
numColumns={2} | |
data={foods} | |
renderItem={({ item }) => <Card food={item} />} | |
/> | |
</SafeAreaView> | |
) | |
} | |
const styles = StyleSheet.create({ | |
Header: { | |
marginTop: 20, | |
flexDirection: "row", | |
justifyContent: "space-between", | |
paddingHorizontal: 20, | |
}, | |
InputContainer: { | |
flex: 1, | |
flexDirection: "row", | |
height: 50, | |
borderRadius: 10, | |
backgroundColor: COLORS.light, | |
alignItems: "center", | |
paddingHorizontal: 20, | |
}, SortBtn: { | |
width: 50, | |
height: 50, | |
marginLeft: 10, | |
backgroundColor: COLORS.primary, | |
borderRadius: 10, | |
justifyContent: "center", | |
alignItems: "center" | |
}, | |
CategoriesListContainer: { | |
paddingVertical: 30, | |
paddingHorizontal: 20, | |
alignItems: "center", | |
}, | |
CategoryBtn: { | |
flexDirection: "row", | |
width: 120, | |
height: 50, | |
marginRight: 7, | |
borderRadius: 30, | |
alignItems: "center", | |
paddingHorizontal: 5, | |
}, | |
CategoryBtnImg: { | |
width: 35, | |
height: 35, | |
backgroundColor: COLORS.white, | |
borderRadius: 20, | |
justifyContent: "center", | |
alignItems: "center" | |
}, | |
Card: { | |
width: cardWidth, | |
height: 220, | |
marginHorizontal: 10, | |
marginTop: 50, | |
marginBottom: 20, | |
borderRadius: 15, | |
elevation: 13, | |
backgroundColor: COLORS.white, | |
}, | |
AddToCartBtn: { | |
width: 30, | |
height: 30, | |
borderRadius: 15, | |
backgroundColor: COLORS.primary, | |
justifyContent: "center", | |
alignItems: "center", | |
}, | |
}) | |
export default HomeScreen | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src\views\screens\OnBoardScreen.js | |
import { StyleSheet, Text, View, Image, Button } from 'react-native'; | |
import React from 'react'; | |
import { SafeAreaView } from 'react-native-safe-area-context'; | |
import COLORS from '../../consts/colors'; | |
import { PrimaryButton } from '../components/Button'; | |
// 登入畫面 | |
const OnBoardScreen = ({ navigation }) => { | |
return ( | |
<SafeAreaView style={{ flex: 1, backgroundColor: COLORS.white }}> | |
<View style={{ height: 400, }}> | |
<Image | |
source={require("../../assets/onboardImage.png")} | |
style={{ width: "100%", resizeMode: "contain", top: -180 }} | |
/> | |
</View> | |
<View style={styles.TextContainer}> | |
<View> | |
<Text style={{ fontSize: 32, fontWeight: "bold", textAlign: "center" }}>Delicious Food</Text> | |
<Text style={{ | |
marginTop: 20, | |
fontSize: 18, | |
textAlign: "center", | |
color: COLORS.grey | |
}}> We help you to find best and delicious food</Text> | |
</View> | |
{/* indicator container */} | |
<View style={styles.IndicatorContainer}> | |
<View style={styles.CurrentIndicator}></View> | |
<View style={styles.Indicator}></View> | |
<View style={styles.Indicator}></View> | |
</View> | |
<PrimaryButton title="Get Started" | |
onPress={() => navigation.navigate("Home")} | |
/> | |
</View> | |
</SafeAreaView> | |
) | |
} | |
const styles = StyleSheet.create({ | |
TextContainer: { | |
flex: 1, | |
paddingHorizontal: 50, | |
justifyContent: "space-between", | |
paddingBottom: 40, | |
}, | |
IndicatorContainer: { | |
flex: 1, | |
height: 50, | |
justifyContent: "center", | |
flexDirection: "row", | |
alignItems: "center", | |
}, | |
CurrentIndicator: { | |
width: 30, | |
height: 12, | |
borderRadius: 10, | |
backgroundColor: COLORS.primary, | |
marginHorizontal: 5, | |
}, | |
Indicator: { | |
width: 12, | |
height: 12, | |
borderRadius: 6, | |
backgroundColor: COLORS.grey, | |
marginHorizontal: 5, | |
} | |
}) | |
export default OnBoardScreen |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment