ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 13. 스택 네비게이터를 이용하여 네비게이션하기
    프로젝트/할일목록 앱 (RN) 2023. 10. 26. 11:58
    728x90
    /**
     * @format
     */
    
    import {AppRegistry} from 'react-native';
    import stackRouter from './stackRouter';
    import {name as appName} from './app.json';
    
    AppRegistry.registerComponent(appName, () => stackRouter);

    루트 디렉토리의 index.js 파일을 위와 같이 수정한다. LandingScreen 대신에 stackRouter 컴포넌트를 화면에 보여준다. 

    import React, { useState, useEffect } from 'react'
    import { View, Text, StyleSheet, ActivityIndicator } from 'react-native'
    import { NavigationContainer } from '@react-navigation/native';
    import { createNativeStackNavigator } from '@react-navigation/native-stack';
    
    import LandingScreen from './screens/LandingScreen';
    import App from './App'
    
    const Stack = createNativeStackNavigator()
    
    function stackRouter(){
        return (
            <NavigationContainer>
                <Stack.Navigator 
                    initialRouteName = "Landing" 
                    screenOptions={{headerShown: false}} // 탭메뉴 헤더와 겹치지 않도록 함
                >
                    <Stack.Screen name="Landing" component={LandingScreen}/>
                    <Stack.Screen name="App" component={App}/>
                </Stack.Navigator>
            </NavigationContainer>
        )
    }
    export default stackRouter

    루트 디렉토리에 stackRouter 컴포넌트를 생성하고 위와 같이 작성한다. 

    import LandingScreen from './screens/LandingScreen';
    import App from './App'

    랜딩페이지와 App 컴포넌트를 임포트한다.

    <NavigationContainer>
        <Stack.Navigator 
            initialRouteName = "Landing" 
            screenOptions={{headerShown: false}} // 탭메뉴 헤더와 겹치지 않도록 함
        >
            <Stack.Screen name="Landing" component={LandingScreen}/>
            <Stack.Screen name="App" component={App}/>
        </Stack.Navigator>
    </NavigationContainer>

    랜딩페이지와 App 컴포넌트는 스택 네비게이터를 이용하여 페이지 전환을 할 수 있도록 설정해준다. 

    import React from 'react'
    import { View, Button, StyleSheet } from 'react-native'
    
    function LoginButton({navigation}){
        const gotoHome = () => {
            navigation.navigate('App')
        }
        return (
            <View style={styles.buttonWrapper}>
                <Button title="로그인" onPress={gotoHome}/>
            </View>
        )
    }
    export default LoginButton
    
    const styles = StyleSheet.create({
        buttonWrapper: {
            position: 'absolute',
            left: 0, right: 0,
            bottom: 100,
        }
    })

    components > LoginButton.js 파일을 생성하고 위와 같이 작성한다. 로그인 버튼을 화면에 보여주고, 소셜로그인 기능을 수행할 수 있는 컴포넌트이다. 로그인 버튼을 터치하면 LandingScreen 컴포넌트로부터 전달받은 navigation 객체를 이용하여 탭메뉴가 있는 App 컴포넌트로 이동한다. 이때 navigate 메서드로 전달되는 문자열은 Stack.Screen 컴포넌트에 설정된 name 과 동일한 값이다. 로그인 버튼은 position 을 'absolute' 로 설정하여 화면 바닥에서 100dp 떨어진 위치에 보여준다. 

    import React, { useState } from 'react'
    import { SafeAreaView, View, Text, StyleSheet, StatusBar, ScrollView, Dimensions, ImageBackground  } from 'react-native'
    import LandingPage from '../components/LandingPage'
    import landingData from '../data/landingData'
    import LoginButton from '../components/LoginButton';
    
    function LandingScreen({navigation}){
        const { width, height } = Dimensions.get('window')
        const [currentPageIndex, setCurrentPageIndex] = useState(0)
        console.log('페이지 번호: ', currentPageIndex)
    
        const setCurrentPage = (e) => {
            const { x } = e.nativeEvent.contentOffset // x : 스크롤 위치
            console.log("스크롤 위치: ", x, "화면너비: ", width)
            const nextPageIndex = Math.ceil(x / width) // x / width : 스크롤 위치 / 화면너비 -> 페이지번호
            console.log(nextPageIndex)
            if(nextPageIndex !== currentPageIndex){
                setCurrentPageIndex(nextPageIndex)
            }
        }
        return (
            <>
                <StatusBar hidden></StatusBar>
                <SafeAreaView style={styles.block}>
                    <ScrollView
                        style={{ flex: 1 }}
                        horizontal={true} // 수평 스크롤링
                        scrollEventThrottle={16} // 스크롤 이벤트 감지하는 시간간격 (ms)
                        pagingEnabled={true} // 스크롤시 페이지네이션
                        showsHorizontalScrollIndicator={false} // 스크롤바 숨기기
                        onScroll={setCurrentPage}
                    >
                        {landingData.map((page, index) => (
                            <LandingPage
                                width={width}
                                height={height}
                                {...page}
                                key={index}
                            />
                        ))}
                    </ScrollView>
    
                    <View style={styles.scrollIndicatorWrapper}>
                        {Array(3).fill(0).map((_, index) => (
                            <View key={index} style={[styles.scrollIndicator, { opacity: currentPageIndex === index ? 1: 0.3}]}></View>
                        ))}
                    </View>
    
                    <LoginButton navigation={navigation}/>
                </SafeAreaView>
            </>
        )
    }
    
    const styles = StyleSheet.create({
      block: {
        flex: 1
      },
      scrollIndicatorWrapper:{
        position: 'absolute',
        left: 0, right: 0,
        bottom: 50,
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center'
      },
      scrollIndicator: {
        height: 10,
        width: 10,
        borderRadius: 10 / 2,
        backgroundColor: '#aaa',
        marginLeft: 10,
      },
    
    })
    export default LandingScreen

    screens > LandingScreen.js 파일을 위와 같이 수정한다.

    import LoginButton from '../components/LoginButton';

    랜딩페이지에서 사용자가 로그인할 수 있도록 loginButton 컴포넌트를 임포트한다.

    <LoginButton navigation={navigation}/>

    loginButton 컴포넌트에서 navigation 객체를 이용할 수 있도록 props 로 내려준다. LoginButton 컴포넌트는 스택 네비게이터에 정의되어 있지 않기 때문에 기본적으로 navigation 객체를 전달받지 못한다. 그래서 사용하려면 LandingScreen 컴포넌트에서 전달받아서 내려줘야 한다. 

    export default function App({navigation}) {
     // 중략
    }

    App.js 파일에 해당 부분을 수정한다. App 컴포넌트는 스택 네비게이터에 정의되어 있으므로 navigation 객체를 전달받고 사용할 수 있다. 

    <NavigationContainer>
    	// 중략
    </NavigationContainer>

    App.js 의 해당 부분을 아래와 같이 Fragment 컴포넌트로 변경한다. 이렇게 하는 이유는 이미 스택 네비게이터를 사용할때 NavigationContainer 로 감싸주었기 때문에 App 컴포넌트에서 다시 NavigationContainer 를 사용하면 에러가 발생한다.

    <>
    	// 중략
    </>

     

    * 페이지 전환 테스트하기

    랜딩페이지의 로그인 버튼을 클릭하면 스택 네비게이터에 정의된 App 컴포넌트로 이동한다.

    랜딩페이지 로그인버튼
    랜딩페이지 로그인 버튼 클릭시 화면전환

     

    728x90
Designed by Tistory.