Expo Router v2 を使ってドロワーメニューを作ります。左側から設定画面へのリンクを表示させるようなメニューです。 「Twitterのようなスライドメニュー」と紹介されることもあります。
- “expo”: “~49.0.13”
- “expo-router”: “^2.0.9”,
- “@react-navigation/drawer”: “^6.6.4”
Expo Router のインストール
Expo Router V2 の始め方は以下のリンクを見てコピペするのが一番良いです。
babel.config.js の設定も含めて全部書いてます。公式を見ましょう。
React Navigation の drawer navigatorをインストール
以下のリンクのとおりに、必要なライブラリをインストールします。 babel.config.js に react-native-reanimated/plugin
の設定が必要な点に注意してください。
npx expo install @react-navigation/drawer react-native-gesture-handler react-native-reanimated
npm install @react-navigation/drawer
ディレクトリ構成
以下のようなディレクトリ構成になります。
app
├── (drawer)
│ ├── _layout.tsx
│ ├── home
│ │ ├── _layout.tsx
│ │ └── index.tsx
│ └── settings
│ ├── _layout.tsx
│ └── index.tsx
└── index.tsx
app/index.tsx
import { Redirect, useRootNavigationState } from 'expo-router';
export default function Page() {
const rootNavigationState = useRootNavigationState();
// ルートナビゲーターが準備できている場合にのみ、リダイレクトをレンダリング
if (rootNavigationState?.key == null) return null;
return <Redirect href={'/(drawer)/home'} />;
}
app/(drawer)/_layout.tsx
import { Drawer } from 'expo-router/drawer';
export default function DrawerLayout() {
return <Drawer screenOptions={{ headerShown: false, swipeEdgeWidth: 0 }}></Drawer>;
}
(drawer)
は Groups
と呼ばれる書き方です。
グループ構文()を使えば、URLでセグメントが表示されないようにすることができる。
app/root/home.jsは/root/homeにマッチします。
app/(root)/home.js は /home にマッチします。
これは、URLに追加のセグメントを追加せずにレイアウトを追加するのに便利です。グループはいくつでも追加できます。グループは、アプリのセクションを整理するのにも適しています。以下の例では、app/(app)はメインアプリの場所、app/(aux)は補助ページの場所です。これは、メインアプリの一部である必要はないが、外部リンクしたいページを追加するのに便利です。
https://docs.expo.dev/routing/layouts/#groups
_layout.tsx
について。
デフォルトでは、ルートは画面全体を埋めます。ルート間の移動は、アニメーションなしの全ページ遷移です。ネイティブアプリでは、ユーザーはヘッダーやタブバーのような共有要素がページ間で持続することを期待しています。これらはレイアウト・ルートを使って作成されます。
Expo Routerは、指定されたディレクトリに対して1つのレイアウトルートを追加することをサポートしています。
https://docs.expo.dev/routing/layouts/#create-a-layout-route
app/(drawer)/home/_layout.tsx
import { Stack } from 'expo-router';
export default function HomeLayout() {
return <Stack />;
}
app/(drawer)/home/index.tsx
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import { Drawer } from 'expo-router/drawer';
import { DrawerToggleButton } from '@react-navigation/drawer';
import { Ionicons } from '@expo/vector-icons';
export default function Page() {
return (
<>
<View style={styles.container}>
<Drawer.Screen
options={{
title: 'Home',
headerShown: true,
headerLeft: () => <DrawerToggleButton />,
headerRight: () => (
<TouchableOpacity onPress={() => {}} style={styles.reloadButton}>
<Ionicons name="reload" size={24} color="black" />
</TouchableOpacity>
),
headerRightContainerStyle: {
marginRight: 18,
},
}}
/>
<Text style={{ fontSize: 24 }}>Index page of Home Drawer</Text>
</View>
</>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
reloadButton: {
marginRight: 17,
},
});
app/(drawer)/settings/_layout.tsx
import { Stack } from 'expo-router';
export default function HomeLayout() {
return <Stack />;
}
app/(drawer)/settings/index.tsx
import { Text, StyleSheet, View } from 'react-native';
import { DrawerToggleButton } from '@react-navigation/drawer';
import { Drawer } from 'expo-router/drawer';
export default function Page() {
return (
<View style={styles.container}>
<Drawer.Screen
options={{
title: 'Settings',
headerShown: true,
headerLeft: () => <DrawerToggleButton />,
}}
/>
<Text style={{ fontSize: 24 }}>Index page of Settings Drawer</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});