Help us understand the problem. What is going on with this article?

両サイドからDrawerを出す

やりたいこと

  • 画面の両側から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と表示するだけ。

Home.js
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と同じ。

Left.js
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と表示するだけ。

Right.js
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の入れ子を作る必要があります。
もっともシンプルな(つもりの)例が下。

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';


//左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

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に設置したボタンからも呼び出してみます。

Home.js
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の生成・読み込み順序を変更すればいいのですが、下記のコードはバグがあり、なぜかヘッダの左ボタンがエラーとなり、継続調査中です(分かる人いれば教えてください)。

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';


//左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が最前で表示されることを仕様にするしかない。。。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away