Last active
February 4, 2022 06:02
-
-
Save rs6000/4554b2b835321bb41ef562482c6ad25c to your computer and use it in GitHub Desktop.
React Native UI Example - PlantShop
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
// yt_example_plantui0128/App.js | |
import * as React from 'react'; | |
import { View, Text } from 'react-native'; | |
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 HomeScreen from "./src/view/screens/HomeScreen" | |
import DetailsScreen from "./src/view/screens/DetailsScreen" | |
const Stack = createNativeStackNavigator(); | |
function App() { | |
return ( | |
<NavigationContainer> | |
<StatusBar barStyle="dark-content" backgroundColor={COLORS.green} /> | |
<Stack.Navigator screenOptions={{ header: () => null }}> | |
<Stack.Screen name="Home" component={HomeScreen} /> | |
<Stack.Screen name="Details" 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
// yt_example_plantui0128/src/view/screens/DetailsScreen.js | |
import { StyleSheet, Text, View, Image } from 'react-native'; | |
import React from 'react'; | |
import { SafeAreaView } from 'react-native-safe-area-context'; | |
import Icon from 'react-native-vector-icons/MaterialIcons'; | |
import COLORS from '../../consts/Colors'; | |
const DetailsScreen = ({ navigation, route }) => { | |
const plant = route.params | |
// console.log(plant) | |
return ( | |
<SafeAreaView style={{ flex: 1, backgroundColor: COLORS.white }}> | |
<View style={styles.Header}> | |
{/* 回上一頁的箭頭 圖示 */} | |
<Icon name="arrow-back" size={28} onPress={() => navigation.goBack()} /> | |
<Icon name="shopping-cart" size={28} /> | |
</View> | |
{/* 商品圖 */} | |
<View style={styles.ImageContainer}> | |
<Image | |
source={plant.img} | |
style={{ flex: 1, resizeMode: 'contain' }} | |
/> | |
</View> | |
{/* 商品簡介 */} | |
<View style={styles.DetailsContainer}> | |
<View style={{ | |
marginLeft: 20, | |
flexDirection: "row", | |
alignItems: "flex-end" | |
}}> | |
<View style={styles.Line} /> | |
<Text style={{ fontSize: 18, fontWeight: "bold" }}>Best choice</Text> | |
</View> | |
{/* 商品名稱 */} | |
<View style={{ | |
marginLeft: 20, | |
marginTop: 20, | |
flexDirection: "row", | |
justifyContent: "space-between", | |
alignItems: "center" | |
}}> | |
<Text style={{ fontSize: 22, fontWeight: "bold" }}>{plant.name}</Text> | |
{/* 價錢的標籤區塊 */} | |
<View style={styles.PriceTag}> | |
{/* 價錢 text */} | |
<Text style={{ | |
marginLeft: 15, | |
fontSize: 16, | |
fontWeight: "bold", | |
color: COLORS.white | |
}}>${plant.price} | |
</Text> | |
</View> | |
</View> | |
{/* 商品簡介 區塊 */} | |
<View style={{ | |
paddingHorizontal: 20, | |
marginTop: 10 | |
}}> | |
<Text style={{ fontSize: 20, fontWeight: "bold" }}>About</Text> | |
{/* 商品介紹文字 */} | |
<Text style={{ color: COLORS.grey, fontSize: 16, lineHeight: 22, marginTop: 10 }}>{plant.about}</Text> | |
{/* 商品訂購 區塊 */} | |
<View style={{ marginTop: 20, flexDirection: "row", justifyContent: "space-between" }}> | |
<View style={{ flexDirection: "row", alignItems: "center" }}> | |
<View style={styles.BorderBtn}> | |
<Text style={styles.BorderBtnText}>-</Text> | |
</View> | |
<Text style={{ marginHorizontal: 10, fontSize: 20, fontWeight: "bold" }}>1</Text> | |
<View style={styles.BorderBtn}> | |
<Text style={styles.BorderBtnText}>+</Text> | |
</View> | |
</View> | |
<View style={styles.BuyBtn}> | |
<Text style={{ color: COLORS.white, fontSize: 18, fontWeight: "bold" }}>Buy</Text> | |
</View> | |
</View> | |
</View> | |
</View> | |
</SafeAreaView > | |
); | |
}; | |
export default DetailsScreen; | |
const styles = StyleSheet.create({ | |
Header: { | |
paddingHorizontal: 20, | |
marginTop: 20, | |
flexDirection: "row", | |
justifyContent: "space-between", | |
}, | |
ImageContainer: { | |
flex: 0.3, | |
marginTop: 20, | |
justifyContent: "center", | |
alignItems: "center", | |
}, | |
DetailsContainer: { | |
flex: 0.7, | |
backgroundColor: COLORS.light, | |
marginHorizontal: 7, | |
marginBottom: 7, | |
borderRadius: 20, | |
marginTop: 30, | |
paddingTop: 30, | |
}, | |
Line: { | |
width: 25, | |
height: 2, | |
backgroundColor: COLORS.dark, | |
marginBottom: 5, | |
marginRight: 3, | |
}, | |
PriceTag: { | |
backgroundColor: COLORS.green, | |
width: 80, | |
height: 40, | |
borderTopLeftRadius: 20, | |
borderBottomLeftRadius: 20, | |
justifyContent: "center", | |
}, | |
BorderBtn: { | |
borderColor: COLORS.grey, | |
borderWidth: 1, | |
borderRadius: 5, | |
width: 60, | |
height: 40, | |
justifyContent: "center", | |
alignItems: "center" | |
}, | |
BorderBtnText: { | |
fontSize: 28, | |
fontWeight: "bold", | |
}, | |
BuyBtn: { | |
width: 100, | |
height: 40, | |
backgroundColor: COLORS.green, | |
justifyContent: "center", | |
alignItems: "center", | |
borderRadius: 20, | |
}, | |
}); |
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
// yt_example_plantui0128/src/view/screens/HomeScreen.js | |
import { StyleSheet, Text, View, TouchableOpacity, FlatList, Dimensions, Image } from 'react-native'; | |
import React, { useState } from 'react'; | |
import { SafeAreaView } from 'react-native-safe-area-context'; | |
import COLORS from '../../consts/Colors'; | |
import Icon from 'react-native-vector-icons/MaterialIcons'; | |
import { TextInput } from 'react-native-gesture-handler'; | |
// 商品資料 | |
import plants from '../../consts/Plants'; | |
//商品框 寬度設定 | |
const width = Dimensions.get("screen").width / 2 - 30 | |
const HomeScreen = ({ navigation }) => { | |
const categories = ['POPULAR', 'ORGANIC', 'INDOORS', 'SYNTHETIC']; | |
const [categoryIndex, setCategoryIndex] = useState(0) | |
const CategoryList = () => { | |
return ( | |
<View style={styles.CategoryContainer}> | |
{categories.map((item, index) => ( | |
<TouchableOpacity key={index} | |
activeOpacity={0.8} | |
onPress={() => setCategoryIndex(index)} > | |
<Text style={[styles.CategoryText, categoryIndex == index && styles.CategoryIndexSelected]}>{item}</Text> | |
</TouchableOpacity> | |
))} | |
</View> | |
) | |
} | |
// 商品卡片 plant 要用{} | |
const Card = ({ plant }) => { | |
return ( | |
<TouchableOpacity onPress={() => navigation.navigate("Details", plant)}> | |
<View style={styles.Card} > | |
<View style={{ alignItems: "flex-end" }}> | |
<View style={{ | |
width: 30, | |
height: 30, | |
borderRadius: 15, | |
alignItems: "center", | |
justifyContent: "center", | |
backgroundColor: plant.like | |
? "rgba(245, 42, 42,0.2)" | |
: "rgba(0,0,0,0.2) ", | |
}}> | |
<Icon name="favorite" size={18} color={plant.like ? COLORS.red : COLORS.dark} /> | |
</View> | |
</View> | |
{/* 商品圖 */} | |
<View style={{ height: 100, alignItems: "center" }}> | |
<Image | |
source={plant.img} | |
style={{ flex: 1, resizeMode: 'contain' }} | |
/> | |
</View> | |
{/* 商品名稱 */} | |
<Text style={{ fontSize: 16, fontWeight: "bold", marginTop: 10 }}>{plant.name}</Text> | |
{/* 商品價格 跟 +號 */} | |
<View style={{ flexDirection: "row", justifyContent: "space-between", marginTop: 5 }}> | |
<Text style={{ fontSize: 18, fontWeight: "bold" }} > ${plant.price}</Text> | |
<View style={{ | |
width: 25, | |
height: 25, | |
backgroundColor: COLORS.green, | |
borderRadius: 5, | |
justifyContent: "center", | |
alignItems: "center", | |
}}> | |
{/* 影片直接用 text + 看起來沒有置中對齊 改用icon 看起來比較整齊 */} | |
{/* icon 展示 https://oblador.github.io/react-native-vector-icons/ */} | |
<Icon name="add" style={{ fontSize: 22, fontWeight: "bold", color: COLORS.white }} /> | |
</View> | |
</View> | |
</View > | |
</TouchableOpacity> | |
) | |
} | |
return ( | |
<SafeAreaView style={{ | |
flex: 1, | |
paddingHorizontal: 20, | |
backgroundColor: COLORS.white | |
}}> | |
<View style={styles.Header}> | |
<View> | |
<Text style={{ fontSize: 25, fontWeight: "bold" }}>Welcome to</Text> | |
<Text style={{ fontSize: 38, fontWeight: "bold", color: COLORS.green }}>Plant Shop</Text> | |
</View> | |
<Icon name="shopping-cart" | |
size={28} | |
/> | |
</View> | |
<View style={{ marginTop: 30, flexDirection: "row" }}> | |
<View style={styles.SearchContainer}> | |
<Icon | |
name="search" | |
size={25} | |
style={{ marginLeft: 20 }} | |
/> | |
<TextInput | |
placeholder='請輸入關鍵字' | |
style={styles.TextInput} | |
/> | |
</View> | |
<View style={styles.SortBtn}> | |
<Icon name="sort" size={30} color={COLORS.white} /> | |
</View> | |
</View> | |
<CategoryList /> | |
{/* 商品展示區 */} | |
<FlatList | |
columnWrapperStyle={{ justifyContent: "space-between" }} | |
showsVerticalScrollIndicator={false} | |
contentContainerStyle={{ | |
marginTop: 10, | |
paddingBottom: 50, | |
}} | |
numColumns={2} | |
data={plants} | |
renderItem={({ item }) => { | |
return <Card plant={item} />; | |
}} | |
/> | |
</SafeAreaView> | |
) | |
}; | |
export default HomeScreen; | |
const styles = StyleSheet.create({ | |
Header: { | |
marginTop: 20, | |
flexDirection: "row", | |
justifyContent: "space-between", | |
}, | |
SearchContainer: { | |
height: 50, | |
backgroundColor: COLORS.light, | |
borderRadius: 10, | |
// 讓兩個並排的關鍵 | |
flex: 1, | |
flexDirection: "row", | |
alignItems: "center", | |
}, | |
TextInput: { | |
marginLeft: 10, | |
fontSize: 18, | |
fontWeight: "bold", | |
color: COLORS.dark, | |
flex: 1, | |
}, | |
SortBtn: { | |
marginLeft: 10, | |
width: 50, | |
height: 50, | |
backgroundColor: COLORS.green, | |
justifyContent: "center", | |
alignItems: "center", | |
borderRadius: 10, | |
}, | |
CategoryContainer: { | |
flexDirection: "row", | |
marginTop: 30, | |
marginBottom: 20, | |
justifyContent: "space-between", | |
}, | |
CategoryText: { | |
fontSize: 16, | |
fontWeight: "bold", | |
color: COLORS.grey, | |
}, | |
CategoryIndexSelected: { | |
color: COLORS.green, | |
paddingBottom: 5, | |
borderBottomWidth: 2, | |
borderColor: COLORS.green, | |
}, | |
Card: { | |
width, | |
height: 225, | |
backgroundColor: COLORS.light, | |
marginHorizontal: 2, | |
borderRadius: 10, | |
marginBottom: 20, | |
padding: 15, | |
}, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment