やりたいこと
- 画面の両側からDrawerを出す(普通は左から)

前提
- React Native利用
- React-Navigation(V4)利用
注意点
2019年9月12日現在、expoでReact-Navigationを利用しようとするとインストールにいろいろと注意が必要。
詳しくはこちらを。
ファイルの準備
App.jsと同じ階層にscreensというフォルダを作り、その中に、以下のファイルを作成する。
- Home.js → 普通のコンテンツ表示
- Left.js → 左Drawerとするページ(カスタム前提)
- Right.js → 右Drawerとするページ(カスタム前提)
各ファイルの準備
Home.js
ただ、Homeと表示するだけ。
import React from 'react';
import { View, Text, Button } from 'react-native';
class Home extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home</Text>
</View>
);
}
}
export default Home;
Left.js
ただ、Leftと表示するだけ。基本Homeと同じ。
import React from 'react';
import { View, Text, Button } from 'react-native';
class Home extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Left</Text>
</View>
);
}
}
export default Home;
Right.js
Rightと表示するだけ。
import React from 'react';
import { View, Text, Button } from 'react-native';
class Right extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Right</Text>
</View>
);
}
}
export default Right;
実装(基本形)
App.js
ここで出し方を定義する。
ややこしいのは、最後の表示できるコンポーネントは1つだけなので、Drawerの入れ子を作る必要があります。
もっともシンプルな(つもりの)例が下。
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { createAppContainer } from 'react-navigation';
import { createDrawerNavigator, DrawerActions } from 'react-navigation-drawer';
import { createStackNavigator } from 'react-navigation-stack';
import Icon from 'react-native-vector-icons/FontAwesome';
//画面読み込み
import Home from './screens/Home';
import Left from './screens/Left';
import Right from './screens/Right';
//左Drawer
const LeftDrawer = createDrawerNavigator(
{
LEFT: Home,
},
{
contentComponent: Left,
drawerPosition: 'left',
}
);
//右Drawer
const RightDrawer = createDrawerNavigator(
{
stack: LeftDrawer,
},
{
drawerPosition: 'right',
contentComponent: Right,
}
);
//AppContainer作成
const AppContainer = createAppContainer(RightDrawer);
export default class App extends React.Component {
render() {
return (
<View style={{ flex: 1 }}>
<AppContainer />
</View>
);
}
}
応用1:ヘッダにアイコンを設置し、そこから(も)Drawerを開く
完成イメージ
ヘッダつけて、そこからDrawerOpen。コンテンツ中のボタンからもOpen。

解説
Drawerをボタンクリック等から開く場合、openDrawer()が使えるのですが、Drawerが複数ある場合うまく機能しません。
そこで、getCustomActionCreatorsを利用して、各DrawerをOpenするためのカスタムメソッドを定義し、それを呼び出す形をとります。
なお、アイコンを表示できるようvector-iconsをインストールしておきます。
npm install --save react-native-vector-icons
実装します。
App.js
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { createAppContainer } from 'react-navigation';
import { createDrawerNavigator, DrawerActions } from 'react-navigation-drawer';
import { createStackNavigator } from 'react-navigation-stack';
import Icon from 'react-native-vector-icons/FontAwesome';
//画面読み込み
import Home from './screens/Home';
import Left from './screens/Left';
import Right from './screens/Right';
//HomeをStackでラップ(ヘッダを表示するため)
const Stack = createStackNavigator(
{
Home: {
screen: Home,
navigationOptions: ({ navigation }) => ({
headerLeft: (<Icon name="bars" size={24} onPress={() => navigation.toggleLeftDrawer()} style={{ paddingLeft: 20 }} />),
headerRight: (<Icon name="cog" size={24} onPress={() => navigation.toggleRightDrawer()} style={{ paddingRight: 20 }} />),
})
}
}
);
//左Drawer
const LeftDrawer = createDrawerNavigator(
{
LEFT: Stack,
},
{
contentComponent: Left,
drawerPosition: 'left',
getCustomActionCreators: (route, stateKey) => {
// console.log("LEFT" + stateKey);
return {
toggleLeftDrawer: () => DrawerActions.toggleDrawer({ key: stateKey }),
};
},
}
);
//右Drawer
const RightDrawer = createDrawerNavigator(
{
stack: LeftDrawer,
},
{
drawerPosition: 'right',
contentComponent: Right,
getCustomActionCreators: (route, stateKey) => {
// console.log("RIGHT" + stateKey)
return {
toggleRightDrawer: () => DrawerActions.toggleDrawer({ key: stateKey }),
};
},
}
);
//AppContainer作成
const AppContainer = createAppContainer(RightDrawer);
export default class App extends React.Component {
render() {
return (
<View style={{ flex: 1 }}>
<AppContainer />
</View>
);
}
}
ヘッダーアイコンだけでなく、Homeに設置したボタンからも呼び出してみます。
import React from 'react';
import { View, Text, Button } from 'react-native';
class Home extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home</Text>
<Button
title="open left drawer"
onPress={() => this.props.navigation.toggleLeftDrawer()}
/>
<Button
title="open right drawer"
onPress={() => this.props.navigation.toggleRightDrawer()}
/>
</View>
);
}
}
export default Home;
応用2:DrawerがHearの下に表示されるようにしてみる(バグ?あり)
応用1では、DrawerがStackNavigatorのヘッダの上に来ます(これはレンダリングの順番に依存)。
場合により、ヘッダはそのまま表示したいときもあるので、その場合のコードを書いてます。
App.jsにおける各種Navigatorの生成・読み込み順序を変更すればいいのですが、下記のコードはバグがあり、なぜかヘッダの左ボタンがエラーとなり、継続調査中です(分かる人いれば教えてください)。
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { createAppContainer } from 'react-navigation';
import { createDrawerNavigator, DrawerActions } from 'react-navigation-drawer';
import { createStackNavigator } from 'react-navigation-stack';
import Icon from 'react-native-vector-icons/FontAwesome';
//画面読み込み
import Home from './screens/Home';
import Left from './screens/Left';
import Right from './screens/Right';
//左Drawer
const LeftDrawer = createDrawerNavigator(
{
left: Home,
},
{
contentComponent: Left,
drawerPosition: 'left',
getCustomActionCreators: (route, stateKey) => {
console.log("LEFT" + stateKey);
return {
toggleLeftDrawer: () => DrawerActions.toggleDrawer({ key: stateKey }),
};
},
}
);
//右Drawer
const RightDrawer = createDrawerNavigator(
{
right: LeftDrawer,
},
{
drawerPosition: 'right',
contentComponent: Right,
getCustomActionCreators: (route, stateKey) => {
console.log("RIGHT" + stateKey)
return {
toggleRightDrawer: () => DrawerActions.toggleDrawer({ key: stateKey }),
};
},
}
);
const Stack = createStackNavigator(
{
Home: {
screen: RightDrawer,
navigationOptions: ({ navigation }) => ({
headerLeft: (<Icon name="bars" size={24} onPress={() => navigation.toggleLeftDrawer()} style={{ paddingLeft: 20 }} />),
headerRight: (<Icon name="cog" size={24} onPress={navigation.toggleRightDrawer} style={{ paddingRight: 20 }} />),
})
}
}
);
//AppContainer作成
const AppContainer = createAppContainer(Stack);
export default class App extends React.Component {
render() {
return (
<View style={{ flex: 1 }}>
<AppContainer />
</View>
);
}
}
バグ?(継続調査中)
なぜか、ヘッダー左のボタン(ここではハンバーガーアイコン)をクリックするとnavigation.toggleLeftDrawer is not a function.エラーがでます。今わかってる現象は、
- navigationの中身をみてもどうやらtoggleLeftDrawerメソッドが追加されていない。
- が、this.props.navigation.toggleLeftDrawer()は実行可能。
読み込みのタイミング次第なのかな?と思いつつ。なぜそうなるか不明。
とりあえず、Drawerが最前で表示されることを仕様にするしかない。。。