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
いえーい
なるほどなるほど
(tabs)
の中にページ作ればいいのねー
https://docs.expo.dev/router/advanced/tabs/
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',
},
});
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>
);
}
めっちゃ簡単じゃん!!
ん?
でもtabに行く前にログイン画面を用意したい時はどうすればいいんだ?
appの中には_layout.tsxしかないしなー
もしかして
app
├── (tabs)
│ ├── _layout.tsx
│ ├── explore.tsx
│ ├── index.tsx
│ └── setting.tsx
├── +not-found.tsx
├── signin.tsx
└── _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]
違うらしい
こうか!
~ 略 ~
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)が表示されている)
Few Years Later...
んん?
stackのドキュメントにはapp/index.tsx
が存在している...
https://docs.expo.dev/router/advanced/stack
app/(tabs)/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 ページを作成(内容は適当)
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 ページを作成(内容は適当)
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
いえーい
うまくいったぜー
まとめ
tabs
のドキュメント読んでたらディレクトリ構造が固定じゃないといけないと思ってしまうね。bottom tabを簡単に実装できていい感じ。
https://docs.expo.dev/router/advanced/tabs/#get-started