Created
February 10, 2022 22:25
-
-
Save rs6000/1130a1c8310a1f3efab1d86e1c36a452 to your computer and use it in GitHub Desktop.
React Native UI Example - Pet
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
import 'react-native-gesture-handler'; | |
import React from 'react'; | |
import { NavigationContainer } from '@react-navigation/native'; | |
import { createNativeStackNavigator } from '@react-navigation/native-stack'; | |
import DrawerNavigator from './src/views/navigators/DrawerNavigator'; | |
import DetailsScreen from './src/views/screens/DetailsScreen'; | |
const Stack = createNativeStackNavigator(); | |
const App = () => { | |
return ( | |
<NavigationContainer> | |
<Stack.Navigator screenOptions={{ headerShown: false }}> | |
<Stack.Screen name="HomeScreen" component={DrawerNavigator} /> | |
<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
// YT_Example_PetUI0204\src\const\colors.js | |
const COLORS = { | |
primary: '#306060', | |
secondary: '#88b3b5', | |
white: '#FFF', | |
dark: '#616161', | |
light: '#f5f5f5', | |
grey: '#a8a8a8', | |
background: '#d0d8dc', | |
orange: '#f5a623', | |
green: '#00B761', | |
}; | |
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
// YT_Example_PetUI0204\src\views\screens\DetailsScreen.js | |
import { StyleSheet, Text, View, SafeAreaView, StatusBar, Image, ImageBackground } from 'react-native'; | |
import React from 'react'; | |
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; | |
import COLORS from "../../const/colors" | |
import { Colors } from 'react-native/Libraries/NewAppScreen'; | |
const DetailsScreen = ({ navigation, route }) => { | |
// 這邊注意 怎麼跟上一頁(homescreen) 拿資料到這一頁來. | |
const pet = route.params; | |
console.log("pet= ", pet) | |
return ( | |
<SafeAreaView style={{ flex: 1, backgroundColor: COLORS.white }}> | |
<StatusBar backgroundColor={COLORS.background} /> | |
{/* 標頭 */} | |
<View style={{ height: 400, backgroundColor: COLORS.background }}> | |
{/* 背景圖 */} | |
<ImageBackground | |
source={pet?.image} | |
resizeMode="contain" style={{ height: 280, top: 20 }} | |
> | |
<View style={styles.Header}> | |
<Icon name="arrow-left" size={28} color={COLORS.dark} onPress={navigation.goBack} /> | |
<Icon name="dots-vertical" size={28} color={COLORS.dark} /> | |
</View> | |
</ImageBackground> | |
{/* 中間的資訊欄 */} | |
<View style={styles.DetailContainer}> | |
<View style={{ | |
flexDirection: "row", | |
justifyContent: "space-between" | |
}}> | |
<Text style={{ | |
fontSize: 20, | |
color: COLORS.dark, | |
fontWeight: "bold", | |
}}> | |
{pet?.name}</Text> | |
<Icon name="gender-male" size={25} color={COLORS.dark} /> | |
</View> | |
<View style={{ | |
flexDirection: "row", | |
justifyContent: "space-between", | |
marginTop: 5, | |
}}> | |
<Text style={{ | |
fontSize: 12, | |
color: COLORS.dark, | |
}}>{pet?.type}</Text> | |
<Text style={{ | |
fontSize: 13, | |
color: COLORS.dark, | |
}}>{pet?.age}</Text> | |
</View> | |
<View style={{ flexDirection: "row", marginTop: 5 }}> | |
<Icon name='map-marker' size={20} color={COLORS.primary} /> | |
<Text style={{ fontSize: 14, marginLeft: 5, color: COLORS.grey }}>5 Green Swamp Road, Nyora, New South Wales, 2646 Australia</Text> | |
</View> | |
</View> | |
</View> | |
{/* 下方資訊欄 */} | |
<View style={{ flex: 1, marginTop: 80, justifyContent: "space-between" }}> | |
<View> | |
<View style={{ flexDirection: "row", paddingHorizontal: 20 }}> | |
{/* 左邊:頭像 */} | |
<Image source={require("../../assets/person.png")} | |
style={{ | |
width: 40, | |
height: 40, | |
borderRadius: 20 | |
}} | |
/> | |
{/* 右邊: 個人資訊 */} | |
<View style={{ flex: 1, paddingLeft: 10, height: 20 }}> | |
<Text style={{ fontSize: 12, color: COLORS.dark, fontWeight: "bold" }}>Smile Hsu</Text> | |
<Text style={{ fontSize: 11, color: COLORS.grey, fontWeight: "bold", marginTop: 2 }}>Owner</Text> | |
</View> | |
{/* 注意: 這邊文字一開始無法顯示 是要去 最上層 SafeAreaView 把 flex:1 加上去才正常 */} | |
<Text style={{ fontSize: 12, color: COLORS.grey }}>Feb 10, 2022</Text> | |
</View> | |
<Text style={styles.Comment}>Lorem ipsum dolor sit amet consectetur adipisicing elit. Minima id exercitationem impedit eveniet nobis molestias nostrum dolorem officiis?</Text> | |
</View> | |
{/* 頁尾 按鈕 */} | |
<View style={styles.Footer}> | |
<View style={styles.IconContainer}> | |
<Icon name='heart-outline' size={22} color={COLORS.white} /> | |
</View> | |
<View style={styles.Btn}> | |
<Text style={{ color: COLORS.white, fontWeight: "bold" }}>ADOPTION</Text> | |
</View> | |
</View> | |
</View> | |
</SafeAreaView> | |
); | |
}; | |
const styles = StyleSheet.create({ | |
Header: { | |
flexDirection: 'row', | |
justifyContent: "space-between", | |
padding: 20, | |
}, DetailContainer: { | |
flex: 1, | |
height: 120, | |
backgroundColor: COLORS.white, | |
padding: 20, | |
marginHorizontal: 20, | |
bottom: -60, | |
elevation: 10, | |
borderRadius: 20, | |
justifyContent: "center", | |
}, | |
Comment: { | |
marginTop: 10, | |
fontSize: 12.5, | |
color: COLORS.dark, | |
marginHorizontal: 20, | |
lineHeight: 20, | |
}, | |
Footer: { | |
flexDirection: "row", | |
height: 100, | |
backgroundColor: COLORS.light, | |
borderTopRightRadius: 30, | |
borderTopLeftRadius: 30, | |
alignItems: "center", | |
paddingHorizontal: 20, | |
}, | |
IconContainer: { | |
backgroundColor: COLORS.primary, | |
width: 50, | |
height: 50, | |
borderRadius: 12, | |
justifyContent: "center", | |
alignItems: "center", | |
marginTop: 15 | |
}, | |
Btn: { | |
flex: 1, | |
backgroundColor: COLORS.primary, | |
height: 50, | |
borderRadius: 12, | |
justifyContent: "center", | |
alignItems: "center", | |
marginTop: 15, | |
marginLeft: 20, | |
}, | |
}); | |
export default DetailsScreen; |
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_PetUI0204\src\views\screens\HomeScreen.js | |
import React, { useEffect, useState } from 'react'; | |
import { | |
Dimensions, | |
StyleSheet, | |
Text, | |
View, | |
SafeAreaView, | |
Image, | |
TextInput, | |
TouchableOpacity, | |
FlatList | |
} from 'react-native'; | |
import { ScrollView } from 'react-native-virtualized-view'; | |
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; | |
import COLORS from "../../const/colors" | |
//寵物資料的api | |
import pets from '../../const/pets'; | |
// | |
const { height } = Dimensions.get("window") | |
//寵物分類的選單項目 | |
const petCategories = [ | |
{ name: 'CATS', icon: 'cat' }, | |
{ name: 'DOGS', icon: 'dog' }, | |
{ name: 'BIRDS', icon: 'ladybug' }, | |
{ name: 'BUNNIES', icon: 'rabbit' }, | |
]; | |
// 寵物卡片 | |
const Card = ({ pet, navigation }) => { | |
return ( | |
<TouchableOpacity activeOpacity={0.8} onPress={() => navigation.navigate("DetailsScreen", pet)}> | |
<View style={styles.CardContainer}> | |
{/* 寵物的圖片 */} | |
<View style={styles.CardImageContainer}> | |
<Image source={pet.image} | |
style={{ width: "100%", height: "100%", resizeMode: "contain" }} | |
/> | |
</View> | |
{/* 寵物的簡介 */} | |
<View style={styles.CardDetailContainer}> | |
<View style={{ flexDirection: "row", justifyContent: "space-between" }}> | |
<Text style={{ color: COLORS.dark, fontSize: 20, fontWeight: "bold" }}>{pet?.name}</Text> | |
<Icon name='gender-male' size={22} color={COLORS.grey} | |
/> | |
</View> | |
<Text style={{ fontSize: 12, marginTop: 5, color: COLORS.dark }}>{pet?.type}</Text> | |
<Text style={{ fontSize: 10, marginTop: 5, color: COLORS.grey }}>{pet?.age}</Text> | |
<View style={{ flexDirection: "row", marginTop: 5 }}> | |
<Icon name='map-marker' size={18} color={COLORS.primary} /> | |
<Text style={{ fontSize: 12, marginLeft: 5, color: COLORS.primary }}>Distance:7.8km</Text> | |
</View> | |
</View> | |
</View> | |
</TouchableOpacity > | |
) | |
} | |
const HomeScreen = ({ navigation }) => { | |
// | |
const [selectedCategoryIndex, setSelectedCategoryIndex] = useState(0) | |
const [filteredPets, setFilteredPet] = useState([]) | |
//篩選寵物 | |
const filterPet = (index) => { | |
const currentPets = pets.filter((item) => item?.pet?.toLocaleUpperCase() == petCategories[index].name)[0].pets | |
// console.log("currentPets =", currentPets[0]) | |
setFilteredPet(currentPets) | |
} | |
// | |
useEffect(() => { | |
filterPet(0) | |
}, []) | |
return ( | |
<SafeAreaView style={{ flex: 1, backgroundColor: COLORS.white }}> | |
{/* 標頭 header */} | |
<View style={styles.Header}> | |
<Icon name="sort-variant" size={28} onPress={navigation.toggleDrawer} /> | |
<Text style={{ color: COLORS.primary, fontSize: 16, fontWeight: "bold" }}>Smile Hsu</Text> | |
<Image source={require(".././../assets/person.png")} style={{ width: 30, height: 30, borderRadius: 15 }} /> | |
</View> | |
{/* 主要內容 main */} | |
<ScrollView showsVerticalScrollIndicator={false}> | |
<View style={styles.MainContainer}> | |
{/* 搜尋列 search bar */} | |
<View style={styles.SearchBarContainer}> | |
<Icon name='magnify' size={24} color={COLORS.grey} /> | |
{/* flex:1 讓字靠右 */} | |
<TextInput | |
placeholder='Search pet to adopt' | |
placeholderTextColor={COLORS.grey} | |
style={{ flex: 1 }} /> | |
<Icon name="sort-ascending" size={24} color={COLORS.grey} /> | |
</View> | |
{/* 寵物分類選單 */} | |
<View style={{ | |
flexDirection: "row", | |
justifyContent: "space-between", | |
marginTop: 20, | |
}}> | |
{/* 用map輸出 寵物分類選單的項目 */} | |
{petCategories.map((item, index) => ( | |
// 讓icon跟下方文字 置中對齊 要寫在這邊 | |
<View key={"pet" + index} style={{ alignItems: "center" }}> | |
{/* 這邊注意 點選icon 會改變顏色 的寫法 */} | |
{/* 注意 onpress 加入 filterPet(index) 第一次用到 大刮號 加兩個函式 */} | |
<TouchableOpacity style={[styles.CategoryBtn, { backgroundColor: selectedCategoryIndex == index ? COLORS.primary : COLORS.white }]} | |
onPress={() => { | |
filterPet(index) | |
setSelectedCategoryIndex(index) | |
}} | |
> | |
<Icon name={item.icon} size={30} color={selectedCategoryIndex == index ? COLORS.white : COLORS.primary} /> | |
</TouchableOpacity> | |
<Text style={styles.CategoryBtnName}>{item.name}</Text> | |
</View> | |
))} | |
</View> | |
{/* 下方的寵物展示 內容區塊 */} | |
<View style={{ marginTop: 20 }}> | |
<FlatList | |
showsVerticalScrollIndicator={false} | |
data={filteredPets} | |
renderItem={({ item }) => <Card pet={item} navigation={navigation} />} | |
/> | |
</View> | |
</View> | |
</ScrollView> | |
</SafeAreaView> | |
); | |
}; | |
const styles = StyleSheet.create({ | |
Header: { | |
padding: 20, | |
flexDirection: "row", | |
justifyContent: "space-between", | |
alignItems: "center", | |
}, | |
MainContainer: { | |
minHeight: height, | |
backgroundColor: COLORS.light, | |
marginTop: 20, | |
borderTopLeftRadius: 40, | |
borderTopRightRadius: 40, | |
paddingHorizontal: 20, | |
paddingVertical: 40, | |
}, | |
SearchBarContainer: { | |
flexDirection: "row", | |
height: 50, | |
backgroundColor: COLORS.white, | |
borderRadius: 7, | |
paddingHorizontal: 20, | |
justifyContent: "space-between", | |
alignItems: "center", | |
}, | |
CategoryBtn: { | |
width: 50, | |
height: 50, | |
justifyContent: "center", | |
alignItems: "center", | |
borderRadius: 10, | |
backgroundColor: COLORS.primary, | |
}, | |
CategoryBtnName: { | |
color: COLORS.dark, | |
fontSize: 10, | |
fontWeight: "bold", | |
marginTop: 5, | |
}, | |
CardContainer: { | |
flexDirection: "row", | |
alignItems: "center", | |
marginBottom: 20, | |
}, CardImageContainer: { | |
width: 140, | |
height: 150, | |
backgroundColor: COLORS.background, | |
borderRadius: 20, | |
}, | |
CardDetailContainer: { | |
flex: 1, | |
height: 120, | |
backgroundColor: COLORS.white, | |
borderTopRightRadius: 10, | |
borderBottomRightRadius: 10, | |
padding: 20, | |
justifyContent: "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
// YT_Example_PetUI0204\src\const\pet.js | |
const pets = [ | |
{ | |
pet: 'cats', | |
pets: [ | |
{ | |
id: '1', | |
name: 'Lily', | |
image: require('../assets/cat1.png'), | |
type: 'Chausie', | |
age: '5 years old', | |
}, | |
{ | |
id: '2', | |
name: 'Lucy', | |
image: require('../assets/cat2.png'), | |
type: 'Bobtail', | |
age: '2 years old', | |
}, | |
{ | |
id: '3', | |
name: 'Nala', | |
image: require('../assets/cat3.png'), | |
type: 'Ragamuffin', | |
age: '2 years old', | |
}, | |
], | |
}, | |
{ | |
pet: 'dogs', | |
pets: [ | |
{ | |
id: '1', | |
name: 'Bally', | |
image: require('../assets/dog1.png'), | |
type: 'German Shepherd', | |
age: '2 years old', | |
}, | |
{ | |
id: '2', | |
name: 'Max', | |
image: require('../assets/dog2.png'), | |
type: 'Foxhound', | |
age: '2 years old', | |
}, | |
], | |
}, | |
{ | |
pet: 'birds', | |
pets: [ | |
{ | |
id: '1', | |
name: 'Coco', | |
image: require('../assets/bird1.png'), | |
type: 'Parrot', | |
age: '2 years old', | |
}, | |
{ | |
id: '2', | |
name: 'Alfie', | |
image: require('../assets/bird2.png'), | |
type: 'Parrot', | |
age: '4 years old', | |
}, | |
], | |
}, | |
{ | |
pet: 'bunnies', | |
pets: [ | |
{ | |
id: '1', | |
name: 'Boots', | |
image: require('../assets/bunny1.png'), | |
type: 'Angora', | |
age: '1 years old', | |
}, | |
{ | |
id: '2', | |
name: 'Pookie', | |
image: require('../assets/bunny2.png'), | |
type: 'Angora', | |
age: '1 years old', | |
}, | |
], | |
}, | |
]; | |
export default pets; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment