10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MetapsAdvent Calendar 2024

Day 15

Expo routerのtabsを使いつつログイン画面は残したい

Last updated at Posted at 2024-12-14

Expo便利ですよね。

最近Expo Routerが出てきてReact Navigationとの違いを検証するべく、個人で試していました。

以下回想...

ひとまずプロジェクト作成。

npx create-expo-app@latest

名前はsample-appとしました。

ディレクトリはこんな感じ。

└── sample-app
    app
    ├── (tabs)
    │   ├── _layout.tsx
    │   ├── explore.tsx
    │   └── index.tsx
    ├── +not-found.tsx
    └── _layout.tsx
    ├── app.json
    ├── assets
    ├── components
    ├── constants
    ├── hooks
    ├── node_modules
    ├── package-lock.json
    ├── package.json
    ├── scripts
    ├── README.md
    └── tsconfig.json
    

なるほど。
_layout.tsxが二つあるのか...

npm run ios

スクリーンショット 2024-12-11 15.30.08.png

いえーい

なるほどなるほど
(tabs)の中にページ作ればいいのねー
https://docs.expo.dev/router/advanced/tabs/

app/(tabs)/setting.tsx
import { Image, StyleSheet } from 'react-native';

import ParallaxScrollView from '@/components/ParallaxScrollView';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';

export default function SettingScreen() {
  return (
    <ParallaxScrollView
      headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }}
      headerImage={
        <Image
          source={require('@/assets/images/partial-react-logo.png')}
          style={styles.reactLogo}
        />
      }>
      <ThemedView style={styles.titleContainer}>
        <ThemedText type="title">Setting</ThemedText>
      </ThemedView>
    </ParallaxScrollView>
  );
}

const styles = StyleSheet.create({
  titleContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: 8,
  },
  stepContainer: {
    gap: 8,
    marginBottom: 8,
  },
  reactLogo: {
    height: 178,
    width: 290,
    bottom: 0,
    left: 0,
    position: 'absolute',
  },
});
app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
import React from 'react';
import { Platform } from 'react-native';
import FontAwesome from '@expo/vector-icons/FontAwesome';

import { HapticTab } from '@/components/HapticTab';
import { IconSymbol } from '@/components/ui/IconSymbol';
import TabBarBackground from '@/components/ui/TabBarBackground';
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';

export default function TabLayout() {
  const colorScheme = useColorScheme();

  return (
    <Tabs
      screenOptions={{
        tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
        headerShown: false,
        tabBarButton: HapticTab,
        tabBarBackground: TabBarBackground,
        tabBarStyle: Platform.select({
          ios: {
            // Use a transparent background on iOS to show the blur effect
            position: 'absolute',
          },
          default: {},
        }),
      }}>
      <Tabs.Screen
        name="index"
        options={{
          title: 'Home',
          tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />,
        }}
      />
      <Tabs.Screen
        name="explore"
        options={{
          title: 'Explore',
          tabBarIcon: ({ color }) => <IconSymbol size={28} name="paperplane.fill" color={color} />,
        }}
      />
      // 以下を追加
      <Tabs.Screen
        name="setting"
        options={{
          title: 'Setting',
          tabBarIcon: ({ color }) =>  <FontAwesome size={28} name="cog" color={color} />,
        }}
      />
    </Tabs>
  );
}

Simulator Screenshot - iPhone 16 Plus - 2024-12-11 at 17.05.49.png

めっちゃ簡単じゃん!!

ん?
でもtabに行く前にログイン画面を用意したい時はどうすればいいんだ?

appの中には_layout.tsxしかないしなー

もしかして

app
├── (tabs)
│   ├── _layout.tsx
│   ├── explore.tsx
│   ├── index.tsx
│   └── setting.tsx
├── +not-found.tsx
├── signin.tsx
└── _layout.tsx

こうで

こうか!

app/_layout.tsx
~  ~

export default function RootLayout() {
~ 中略 ~

  return (
    <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
      <Stack>
        // 以下を追加
        <Stack.Screen name="signin"/>
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
        <Stack.Screen name="+not-found" />
      </Stack>
      <StatusBar style="auto" />
    </ThemeProvider>
  );
}
 (NOBRIDGE) WARN  [Layout children]: No route named "signin" exists in nested children: ["+not-found", "_sitemap", "(tabs)"] [Component Stack]

違うらしい

こうか!

app/_layout.tsx
~  ~
  const [display, setDisplay] = useState(false);
  return (
    <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
      {display ? (
        <Stack>
          <Stack.Screen name="(tabs)" options={{headerShown: false}} />
          <Stack.Screen name="signin" />
          <Stack.Screen name="+not-found" />
        </Stack>
      ) : (
        <Stack>
          <Stack.Screen name="signin" />
        </Stack>
      )}
      <StatusBar style="auto" />
    </ThemeProvider>
  );
}

違うねー
(headerに(tabs)が表示されている)

Simulator Screenshot - iPhone 16 Plus - 2024-12-13 at 18.23.32.png

Few Years Later...

んん?
stackのドキュメントにはapp/index.tsxが存在している...
https://docs.expo.dev/router/advanced/stack

app/(tabs)/index.tsx -> app/index.tsxに移動してみるか

内容はこんな感じで

app/index.tsx
import { Button, View } from 'react-native';
import {useRouter} from 'expo-router';

export default function HomeScreen() {
  const router = useRouter();

  return (
    <View>
      <Button
        title="Sign In"
        onPress={() => router.push('/signin')}>
      </Button>
      <Button
        title="Sign Un"
        onPress={() => router.push('/signup')}>
      </Button>
    </View>
  );
}

sign in ページを作成(内容は適当)

app/signin.tsx
import React, {useState} from 'react';
import {View, Button} from 'react-native';
import {useRouter} from 'expo-router';

export default function SignIn() {
  const router = useRouter();

  return (
    <View>
      <Button
        title="Sign In"
        onPress={() => router.push('/setting')}>
      </Button>
    </View>
  );
}

sign up ページを作成(内容は適当)

app/signup.tsx
import React, {useState} from 'react';
import {View} from 'react-native';

export default function SignUp() {
  return (
    <View></View>
  );
}

ディレクトリは最終的にこんな感じ

app
├── (tabs)
│   ├── _layout.tsx
│   ├── explore.tsx
│   └── setting.tsx
├── +not-found.tsx
├── _layout.tsx
├── index.tsx
├── signin.tsx
└── signup.tsx

iPhone 16 Plus Screen Recording Dec 13 2024.gif

いえーい
うまくいったぜー

まとめ

tabsのドキュメント読んでたらディレクトリ構造が固定じゃないといけないと思ってしまうね。bottom tabを簡単に実装できていい感じ。
https://docs.expo.dev/router/advanced/tabs/#get-started

10
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?