ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • <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에 다시올리겠습니당ㅎㅎㅎㅎㅎ

    반응형

    댓글

Designed by Tistory.