注意
- react-navigation v3で動作確認しています
- react-navigation v4がリリースされ、createBottomTabNavigator等が分離されています(未対応)。
概要
まともなアプリを作るためにはNavigation機能の利用は避けて通れません。
ライブラリとしてはreact-navigationとreact-native-router-flux(rnrf)というのが有名みたいですが、Reac自体に不慣れなので情報が多いreact-navigationを利用してみます。
この基礎編をベースにした応用編んも書きました。よろしければどうぞ。
- React-Navigatorを利用してみる(基礎編)→この記事
- RN応用編1:Drawer Navigatorをハンバーガーメニューで表示させる
- RN応用編2:TabやDrawerメニューにアイコンを設定する
- RN応用編3:Reduxで値の取り回し
React Navigation(react-navigation:以下RN)
RNでは3つのナビゲーション機能を提供しています。
- Stack Navigation
- Tab Nabigation
- Drawer Nabigation
このそれぞれの実装方法を見てみます。
準備
作業ディレクトリの作成と必要モジュールのインストール
必要なものをインストールします。
私はexpoを利用していますが、create-react-native-appとかでも動くと思います。
expoがインストールされていない場合は、npm install -g expoで。
expo init navigation
cd navigation
npm install --save react-navigation
npm install
expo24ではreact-native-gesture-handlerに関するWarningがでます。ここを見て解決を。
デモに必要なファイルを生成
移動先として利用するページ(Screen)を先に作っておきます。
mkdir screens
cd screens
touch Tab1.js Tab2.js Stack1.js Stack2.js Single1.js Single2.js
各ファイルと役割
作成したファイルは各Navigationで以下のように利用していく予定です。
各ファイル記述
とりあえず画面遷移だけ確認できればいいので、各ページ名を表示するだけの簡単な記述を行っていきます。
ページ名だけが違うほぼ同じ内容ですが、作業の基本ファイルとなるので、冗長ですが各ファイルの内容を記述しておきます。
screens/Single1.js
import React from 'react';
import { View, Text, Button } from 'react-native';
export default class Single1 extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Single1</Text>
</View>
);
}
}
注意:export defaultする位置
このテンプレートでは、classの宣言と一緒にexportしていますが、後々(Reduxの利用)とかを想定すると、classの宣言とexportは分けてもいいかもしれません。
import React from 'react';
import { View, Text, Button } from 'react-native';
class Single1 extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Single1</Text>
</View>
);
}
}
+export default Single1;
screens/Single2.js
import React from 'react';
import { View, Text, Button } from 'react-native';
export default class Single2 extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Single2</Text>
</View>
);
}
}
screens/Stack1.js
import React from 'react';
import { View, Text, Button } from 'react-native';
export default class Stack1 extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Stack1</Text>
</View>
);
}
}
screens/Stack2.js
import React from 'react';
import { View, Text, Button } from 'react-native';
export default class Stack2 extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Stack2</Text>
</View>
);
}
}
screens/Tab1.js
import React from 'react';
import { View, Text, Button } from 'react-native';
export default class Tab1 extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Tab1</Text>
</View>
);
}
}
screens/Tab2.js
import React from 'react';
import { View, Text, Button } from 'react-native';
export default class Tab2 extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Tab2</Text>
</View>
);
}
}
App.js
記述が完了したらアプリ実行の起点となるApp.jsファイルでscreens/*以下のファイルをまとめてimportしておきます。
で、とりあえず表示を確認したいので、<Single1 />を呼び出す記述を行い動作確認をしてみます。
最近のexpoが出力するテンプレートはファンクションコンポーネントベースになっていますが(おそらくHookが追加されたため)、ここではクラスコンポーネントに書き換えています。
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
//import screens
import Single1 from './screens/Single1';
import Single2 from './screens/Single2';
import Stack1 from './screens/Stack1';
import Stack2 from './screens/Stack2';
import Tab1 from './screens/Tab1';
import Tab2 from './screens/Tab2';
export default class App extends React.Component {
render() {
return (
<Single1/>
);
}
}
動作確認
コンソールで以下のコマンドを打ちます。
expo start
Webが立ち上がるので検証したい環境で検証します。私はiOS Simulatorを選んでます。
動いているようです。
ここまでに用意したテンプレートを利用して各ナビゲーション機能を試してみます。
Stack Navigatorの実装
最も基本的なStack Navigatorを試します。次へ次へと移動するようなやつです。
動作を言葉で表現しづらいですが、まあ、動かせばわかります。
以下、diffモードで差分だけ見ていきます。
動作させる際にはコード中の+は削除してくださいね。念の為。
App.js
ではStack Navigationの実装を行います。
やることは、StackNavigator(ここではStack)の定義をcreateStackNavigator()で行い、それを呼び出すだけですが、createStackNavigatorを利用するためのimport等も記述します。
後で利用するcreateBottomTabNavigatorとcreateDrawerNavigatorもimportしておきます。
なお、React-Navigation V3からAppContainerでラップする必要があります。
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
+import { createStackNavigator, createBottomTabNavigator, createDrawerNavigator, createAppContainer } from 'react-navigation';
//import screens
import Single1 from './screens/Single1';
import Single2 from './screens/Single2';
import Stack1 from './screens/Stack1';
import Stack2 from './screens/Stack2';
import Tab1 from './screens/Tab1';
import Tab2 from './screens/Tab2';
//stack
+const Stack = createStackNavigator(
+ {
+ Stack1: {screen:Stack1},
+ Stack2: {screen: Stack2},
+ },
+ {
+ initialRouteName: 'Stack1'
+ }
+);
export default class App extends React.Component {
render() {
//AppContainerでラップ
+ const Layout = createAppContainer(Stack);
return (
<Layout/>
);
}
}
screens/Stack1.js
- 初期画面となるStack1にStack2への移動ボタンをつけます(Stack1へ戻るボタンは自動生成されます)。
import React from 'react';
import { View, Text, Button } from 'react-native';
export default class Stack1 extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Stack1</Text>
<Button
title='GoTo Stack2'
+ onPress={() => this.props.navigation.navigate('Stack2')}
/>
</View>
);
}
}
では動作確認してみます。
できました。簡単です。
Tab Navigatorの実装
次にTab Navigatorを実装します。
上記で作成したStackのコードを残したままなので長く見えますが、数行追加するだけです。
DrawerでStackを利用するので記述を残しています。
screensの方はいじりません。
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { createStackNavigator, createBottomTabNavigator, createDrawerNavigator, createAppContainer } from 'react-navigation';
//import screens
import Single1 from './screens/Single1';
import Single2 from './screens/Single2';
import Stack1 from './screens/Stack1';
import Stack2 from './screens/Stack2';
import Tab1 from './screens/Tab1';
import Tab2 from './screens/Tab2';
//stack
const Stack = createStackNavigator(
{
Stack1: { screen: Stack1 },
Stack2: { screen: Stack2 },
},
{
initialRouteName: 'Stack1'
}
);
+//Tab
+const Tab = createBottomTabNavigator(
+ {
+ Tab1: { screen: Tab1 },
+ Tab2: { screen: Tab2 },
+ }
+);
export default class App extends React.Component {
render() {
//AppContainerでラップ
+ const Layout = createAppContainer(Tab);
return (
<Layout />
);
}
}
出来ました。こちらも簡単でした。
参考:Tabの要素にStackを入れ子にする
下記のようにするとStackをTabのコンテンツの1つとして扱うこともできます。
ここではソースは変更いませんが、試したい人はどうぞ。
.....
//stack
const Stack = createStackNavigator(
{
Stack1: { screen: Stack1 },
Stack2: { screen: Stack2 },
},
{
initialRouteName: 'Stack1'
}
);
//tab
const Tab = createBottomTabNavigator(
{
+ Tab1: { screen: Stack },
Tab2: { screen: Tab2 }
}
);
.....
参考:Tabの要素をStackNavigatorでラップする
StackNavigatorを利用するとHeader部分に(いい意味でも悪い意味でも)一定のスペースが確保されますが、素のページにはスペースが無いので、見た目がちぐはぐになるため、素ページをStackNavigatorでラップして見た目をととのえます。
例えば下記のようにします。
//Tab
const Tab = createBottomTabNavigator(
{
Tab1: { screen: Stack },
Tab2: { screen: createStackNavigator({ Tab2: { screen: Tab2 } }) },
}
);
Drawer Navigator
最後にDrawer Navigationを実装します。画面左端を右にスワイプ?すれば、出てくる画面です。
口で説明するのが難しいですね。。。まあ、動かせばわかります。横向きのタブ?的なものです。
いくつかのサンプルではヘッダ領域にハンバーガーアイコンを表示させて、それをクリックすることで表示させているものもありますが、基本、アイコン等が無くてもスワイプで開きます(ヘッダのボタンで表示させるのは、応用で書こうかと思います)。
ここでは、今までに開発したStack, Tab, Single1, Single2を全て表示させてみます。
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { createStackNavigator, createBottomTabNavigator, createDrawerNavigator, createAppContainer } from 'react-navigation';
//import screens
import Single1 from './screens/Single1';
import Single2 from './screens/Single2';
import Stack1 from './screens/Stack1';
import Stack2 from './screens/Stack2';
import Tab1 from './screens/Tab1';
import Tab2 from './screens/Tab2';
//stack
const Stack = createStackNavigator(
{
Stack1: { screen: Stack1 },
Stack2: { screen: Stack2 },
},
{
initialRouteName: 'Stack1'
}
);
//Tab
const Tab = createBottomTabNavigator(
{
Tab1: { screen: Tab1 },
Tab2: { screen: createStackNavigator({ Tab2: { screen: Tab2 } }) },
}
);
+//drawer
+const Drawer = createDrawerNavigator(
+ {
+ Stacks: { screen: Stack },
+ Tabs: { screen: Tab },
+ Single1: { screen: Single1 },
+ Single2: { screen: Single2 },
+ },
+ {
+ initialRouteName: 'Tabs'
+ }
+);
export default class App extends React.Component {
render() {
//AppContainerでラップ
+ const Layout = createAppContainer(Drawer);
return (
<Layout />
);
}
}
動作確認してみます。
普通に起動したときにはただStack1が表示されるだけです。画面左端をスワイプしてください。
以上です。動きました。
わかればすごく簡単なのですが、機能の依存関係などを把握するまでに時間がかかりますね。
応用編
基本編をベースにした応用編も書きました。合わせてご覧ください。