-
<react-native>mission 앱 제작1_메인화면, 로그인, 홈 화면, ...app/react-native 2022. 5. 18. 13:31반응형
APP 제작
APP 설명
혼자서는 정해진 일을 하지 못하게 되고, 좋은 습관을 들이지 못하는 사람들을 위해, 오늘 일을 내일로 미루는 사람들을 위해 터치 업 할 수 있도록, 타인과 약속을 만들고 공유하여 능률을 올릴 수 있는 어플
구조설명
DB 관계도
현재까지 구현 page 설명
src
- assets
필요한 image
- components
- core
아직 불필요 - core - theme.tsx만 사용
- screens
보여지는 화면
app.tsx
screens가 보이도록 연결
pakage.json
깔아야하는 패키지를 보여줌
pakage.json
필요한 pakege들을 yarn or npm 으로 설치
app.tsx
src/screen으로 가도록 연결
가장 첫화면은 appstack에 들어가는 login 화면이 나온다
/** * Sample React Native App * https://github.com/facebook/react-native * * @format * @flow */ import React from 'react'; import AppStack from './src/screens'; const App = () => { return ( <AppStack /> ); }; export default App;
src
core
theme.tsx
import { DefaultTheme } from 'react-native-paper'; export const theme = { ...DefaultTheme, colors: { ...DefaultTheme.colors, primary: '#46c3ad', secondary: '#414757', error: '#f13a59', }, };
screens
index.tsx
- 모든 화면에 들어가는 전체화면과 네비게이터들
import React from "react"; import { Text } from "react-native"; import { createAppContainer, createSwitchNavigator } from "react-navigation"; import { createStackNavigator } from "react-navigation-stack"; import { createBottomTabNavigator } from "react-navigation-tabs"; import LoginScreen from "./LoginScreen"; import HomeScreen from "./HomeScreen"; import CreateScreen from "./CreateScreen"; import MissionDetailScreen from "./MissionDetailScreen"; import SettingScreen from "./SettingScreen"; import SomethingScreen from "./SomethingScreen"; import { Navigation } from '../types'; type Props = { navigation: Navigation; } const HomeStack = createStackNavigator( { HomeScreen, }, // if you need. // recommend custom header { defaultNavigationOptions: ({ navigation }) => ({ title: "Hello mission world", }), } ); const SettingStack = createStackNavigator( { SettingScreen, SomethingScreen, }, { defaultNavigationOptions: ({ navigation }) => ({ title: "Setting", }), initialRouteName: "SettingScreen", } ); const TabNavigator = createBottomTabNavigator( { Home: HomeStack, Setting: SettingStack, }, { defaultNavigationOptions: ({ navigation }) => ({ tabBarIcon: ({ focused, horizontal, tintColor }) => { const { routeName } = navigation.state; let icon = "▲"; if (routeName === "Home") { icon = "🌈"; } else if (routeName === "Setting") { icon = "🌙"; } // can use react-native-vector-icons // <Icon name={iconName} size={iconSize} color={iconColor} /> return ( <Text style={{ color: (focused && "#46c3ad") || "#888" }}> {icon} </Text> ); }, }), lazy: false, tabBarOptions: { activeTintColor: "#46c3ad", inactiveTintColor: "#888", }, } ); const AppStack = createStackNavigator({ LoginScreen: LoginScreen, CreateScreen: CreateScreen, MissionDetailScreen: MissionDetailScreen, TabNavigator: { screen: TabNavigator, }, }); export default createAppContainer(AppStack);
types.tsx - 네비게이터 선언
export type Navigation = { navigate: (scene: string) => void; };
- style을 따로 빼놓고 구현했어야했는데 방식을 아직몰라서
아직 화면마다 일일히 구현하였습니다 ㅠ
LoginScreen -index.tsx
import React, {Component, memo} from 'react'; import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen'; import { Navigation } from '../../types'; import {useState } from 'react'; import { TouchableOpacity, StyleSheet, Text, View, Image } from 'react-native'; import Background from '../../components/Background'; import Logo from '../../components/Logo'; import Header from '../../components/Header'; import Button from '../../components/Button'; import TextInput from '../../components/TextInput'; import BackButton from '../../components/BackButton'; import { theme } from '../../core/theme'; import { emailValidator, passwordValidator } from '../../core/utils'; type Props = { navigation: Navigation; } const LoginScreen = ({ navigation }: Props) => { const [email, setEmail] = useState({ value: '', error: '' }); const [password, setPassword] = useState({ value: '', error: '' }); const _onLoginPressed = () => { const emailError = emailValidator(email.value); const passwordError = passwordValidator(password.value); if (emailError || passwordError) { setEmail({ ...email, error: emailError }); setPassword({ ...password, error: passwordError }); return; } navigation.navigate('Dashboard'); }; return ( <View style={styles.container}> <View> <Image style={styles.image} source={require('../../assets/logo.png')} /> </View> <View style={styles.formArea}> <TextInput style={styles.textForm} placeholder={"ID"}/> <TextInput style={styles.textForm} placeholder={"Password"}/> </View> <View style={styles.buttonArea}> <TouchableOpacity onPress={() => navigation.navigate('HomeScreen')}> <Text style={styles.link}>Login</Text> </TouchableOpacity> </View> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: 'white', paddingLeft: wp('10%'), paddingRight: wp('10%'), justifyContent: 'center', }, titleArea: { width: '100%', padding: wp('10%'), alignItems: 'center', }, title: { fontSize: wp('10%'), color: theme.colors.primary, }, formArea: { width: '100%', paddingBottom: wp('10%'), }, textForm: { borderWidth: 0.5, borderColor: '#888', width: '100%', height: hp('5%'), paddingLeft: 5, paddingRight: 5, marginBottom: 5, }, buttonArea: { width: '100%', height: hp('5%'), }, button: { backgroundColor: "#46c3ad", width: "100%", height: "100%", justifyContent: 'center', alignItems: 'center', }, buttonTitle: { color: 'white', }, link: { fontWeight: 'bold', color: theme.colors.primary, }, image: { width: 200 , height : 200 , marginLeft : 50 , marginBottom : 20 } }) export default memo(LoginScreen);
HomeScreen -index.tsx
import React, {Component,memo} from 'react'; import { View, Text, ScrollView, StyleSheet } from 'react-native'; import { TouchableOpacity} from 'react-native'; import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen'; import { Navigation } from '../../types'; import Header from '../../components/Header'; import { theme } from '../../core/theme'; type Props = { navigation: Navigation; } const HomeScreen = ({ navigation }: Props) => { <Header>Home Test</Header> return ( <View style={styles.container}> <TouchableOpacity style={styles.wrapButton} onPress={() => navigation.navigate('MissionDetailScreen')}> <Text> algorithm Mission</Text> </TouchableOpacity> <TouchableOpacity style={styles.CreateButton} onPress={() => navigation.navigate('CreateScreen')}> <Text style={styles.text}>+</Text> </TouchableOpacity> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, padding: wp('5%'), backgroundColor: 'white', }, // wrapContent: { // width: wp('90%'), // height: wp('90%'), // paddingBottom: wp('5%'), // }, // content: { // width: "100%", // height: "100%", // backgroundColor: "#46c3ad", // }, wrapButton: { backgroundColor: '#46c3ad', width: wp('45%'), height: hp('30%'), paddingLeft: wp('8%'), marginTop: 20, justifyContent: 'center', borderBottomWidth: 0.5, borderColor: '#46c3ad', }, CreateButton: { backgroundColor: '#46c3ad', alignItems: 'center', justifyContent: 'center', width: 50, height: 50, marginTop: 200, marginLeft: 300, borderRadius: 35, }, text: { fontSize: 30, textAlign: 'center', color: 'white' } }) export default memo(HomeScreen);
SettingScreen -index.tsx
import React, {Component, memo} from 'react'; import { View, Text, TouchableOpacity, Alert, StyleSheet } from 'react-native'; import { StackActions, NavigationActions } from 'react-navigation'; import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen'; import { Navigation } from '../../types'; import Header from '../../components/Header'; type Props = { navigation: Navigation; } const SettingScreen = ({ navigation }: Props) => { <Header>Setting Screen Test</Header> return ( <View style={styles.container}> <TouchableOpacity style={styles.wrapButton} onPress={() => navigation.navigate('LoginScreen')}> <Text>🏅 Something</Text> </TouchableOpacity> <TouchableOpacity style={styles.wrapButton} onPress={() => navigation.navigate('LoginScreen')}> <Text>🔓 Logout</Text> </TouchableOpacity> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: 'white', }, wrapButton: { width: wp('100%'), height: hp('8%'), paddingLeft: wp('8%'), justifyContent: 'center', borderBottomWidth: 0.5, borderColor: '#ccc', } }) export default memo(SettingScreen);
CreateScreen -index.tsx : 미션 생성 화면
import React, {Component, memo} from 'react'; import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen'; import { Navigation } from '../../types'; import {useState } from 'react'; import { TouchableOpacity, StyleSheet, Text, View, ScrollView, Alert } from 'react-native'; import Background from '../../components/Background'; import Logo from '../../components/Logo'; import Header from '../../components/Header'; import Button from '../../components/Button'; import TextInput from '../../components/TextInput'; import BackButton from '../../components/BackButton'; import { theme } from '../../core/theme'; import { emailValidator, passwordValidator } from '../../core/utils'; import { RadioButton } from 'react-native-paper'; type Props = { navigation: Navigation; } const CreateScreen = ({ navigation }: Props) => { const [value, setValue] = React.useState('first'); return ( <ScrollView> <View style={styles.container}> <View style={styles.titleArea}> <Text style={styles.title}>Create Misson!!</Text> </View> <View style={styles.formArea}> <Text>카테고리를 선택해주세요.</Text> <RadioButton.Group onValueChange={value => setValue(value)} value={value}> <RadioButton.Item label="생활 습관" value="first" /> <RadioButton.Item label="공부/스터디" value="second" /> <RadioButton.Item label="운동/건강" value="third" /> </RadioButton.Group> <Text>최대 인원을 선택해주세요.</Text> <TextInput style={styles.textForm1} placeholder={" 명 "}/> <Text>미션 기간(최대 90일)을 선택해주세요.</Text> <RadioButton.Group onValueChange={value => setValue(value)} value={value}> <RadioButton.Item label="30일" value="first" /> <RadioButton.Item label="60일" value="second" /> <RadioButton.Item label="90일" value="third" /> </RadioButton.Group> <Text>미션 제목을 작성해주세요.</Text> <TextInput style={styles.textForm1} placeholder={" "}/> <Text>미션 내용을 작성해주세요.</Text> <TextInput style={styles.textForm2} placeholder={" "}/> <Text>패널티를 선택해주세요.</Text> <RadioButton.Group onValueChange={value => setValue(value)} value={value}> <RadioButton.Item label="강퇴" value="first" /> <RadioButton.Item label="인증서 미제공" value="second" /> <RadioButton.Item label="랭킹 제외" value="third" /> </RadioButton.Group> </View> <View> <TouchableOpacity onPress={() => navigation.navigate('HomeScreen')}> <Text style={styles.wrapButton}>미션 완성</Text> </TouchableOpacity> </View> </View> </ScrollView> ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: 'white', paddingLeft: wp('10%'), paddingRight: wp('10%'), justifyContent: 'center', }, titleArea: { width: '100%', padding: wp('10%'), alignItems: 'center', }, title: { fontSize: wp('8%'), color: theme.colors.primary, }, formArea: { width: '100%', paddingBottom: wp('10%'), }, textForm1: { borderWidth: 0.5, borderColor: '#888', width: '100%', height: hp('5%'), paddingLeft: 5, paddingRight: 5, marginBottom: 5, }, textForm2: { borderWidth: 0.5, borderColor: '#888', width: '100%', height: hp('20%'), paddingLeft: 5, paddingRight: 5, marginBottom: 5, }, buttonArea: { width: '100%', height: hp('5%'), }, wrapButton: { backgroundColor: '#46c3ad', width: wp('100%'), height: hp('5%'), paddingTop : 10, paddingLeft: wp('30%'), paddingRight: hp('8%'), marginTop: 20, justifyContent: 'center', borderBottomWidth: 0.5, borderColor: '#ccc', }, button: { backgroundColor: "#46c3ad", width: "100%", height: "100%", justifyContent: 'center', alignItems: 'center', }, buttonTitle: { color: 'white', }, link: { fontWeight: 'bold', color: theme.colors.primary, }, scrollView: { backgroundColor: 'orange', marginHorizontal: 20, }, }) export default memo(CreateScreen);
missionDetailScreen - index.tsx : 미션 상세 화면
import React, {Component, memo} from 'react'; import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen'; import { Navigation } from '../../types'; import {useState } from 'react'; import { TouchableOpacity, StyleSheet, Text, View } from 'react-native'; import Background from '../../components/Background'; import Logo from '../../components/Logo'; import Header from '../../components/Header'; import Button from '../../components/Button'; import TextInput from '../../components/TextInput'; import BackButton from '../../components/BackButton'; import { theme } from '../../core/theme'; import { emailValidator, passwordValidator } from '../../core/utils'; type Props = { navigation: Navigation; } const MissionDetailScreen = ({ navigation }: Props) => { return ( <View style={styles.container}> <View style={styles.titleArea}> <Text style={styles.title}>algorithm Mission</Text> </View> <View style={styles.formArea}> <Text>카테고리 : 공부/스터디</Text> <Text>최대 인원 : 15명</Text> <Text>미션 기간 : 90일</Text> <Text>미션 제목 : algorithm Mission </Text> <Text>미션 내용 : 1일 1알고리즘</Text> <Text>패널 티 : 강퇴</Text> </View> <View style={styles.buttonArea}> <TouchableOpacity onPress={() => navigation.navigate('HomeScreen')}> <Text style={styles.link}>미션에 참여하시겠습니까?</Text> </TouchableOpacity> </View> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: 'white', paddingLeft: wp('10%'), paddingRight: wp('10%'), justifyContent: 'center', }, titleArea: { width: '100%', padding: wp('10%'), alignItems: 'center', }, title: { fontSize: wp('6%'), color: theme.colors.primary, }, formArea: { width: '100%', paddingBottom: wp('10%'), }, textForm: { borderWidth: 0.5, borderColor: '#888', width: '100%', height: hp('5%'), paddingLeft: 5, paddingRight: 5, marginBottom: 5, }, buttonArea: { width: '100%', height: hp('5%'), }, button: { backgroundColor: "#46c3ad", width: "100%", height: "100%", justifyContent: 'center', alignItems: 'center', }, buttonTitle: { color: 'white', }, link: { fontWeight: 'bold', color: theme.colors.primary, } }) export default memo(MissionDetailScreen);
SomethingScreen - index.tsx : 로그인 하지 않으면 이용 불가
import React, {Component, memo} from 'react'; import { View, Text, Button, StyleSheet } from 'react-native'; import { Navigation } from '../../types'; import BackButton from '../../components/BackButton'; type Props = { navigation: Navigation; } const SomethingScreen = ({ navigation }: Props) => { return ( <View style={styles.container}> <Text>something</Text> <BackButton goBack={() => navigation.navigate('HomeScreen')} /> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: 'white', justifyContent: 'center', alignItems: 'center', }, }) export default memo(SomethingScreen);
실행결과
결론
구현목적이였어서
아직 불완전한 소스이니 그냥 소스안에서 사용될만한것들만 쓰시고
전체소스는 아직 안쓰시기 바랍니당 ㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜ
코드도 더 깔끔하게 하고 기능 추가해 제작2에 다시올리겠습니당ㅎㅎㅎㅎㅎ
반응형'app > react-native' 카테고리의 다른 글
<react-native>view 컴포넌트와 css 박스 모델/자원과 아이콘 사용하기 (0) 2022.04.11 <react-native> faker / error 확인 (1) 2022.04.09 <react-native> style 속성과 styleSheet API ... (0) 2022.04.09 <react-native> Button, alert, ... (0) 2022.04.09 react-native를 시작하면서/window 개발환경 설정/오류 해결_2 (0) 2022.04.06