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

React Navigationで、特定の画面でボトムタブを非表示にする

More than 1 year has passed since last update.

やりたいこと

React NavigationcreateBottomTabNavigatorを使って画面をタブ表示していて、かつタブ内に createStackNavigatorでスタックナビゲータを入れている時。

このスタックの最初のページ以外はタブバーを非表示にしたいということ、あると思います。

イメージでいうと、こんな感じです。

import {createStackNavigator, createBottomTabNavigator} from 'react-navigation'

const TabAStack =  createStackNavigator({
  ScreenA1,
  ScreenA2, //タブバー非表示にしたい
  ScreenA3, //タブバー非表示にしたい
});

export default createBottomTabNavigator({
  TabAStack,
  TabB,
  TabC
});

今回は、その方法を紹介します。

前提

React Navigationのバージョン

今回試したReact Navigationのバージョンは以下です。

"react-navigation": "^2.9.1"

実現方法

stackNavigatorのnavigationOptionstabBarVisible を指定してあげるだけです。

「どの画面に対して」を指定する場合には、少なくとも以下の2通りがあります。

  1. スタックのindexで切り分ける場合
  2. 画面の名前で切り分ける場合

スタックのindexで切り分ける場合

navigation.state.indexでスタックの何番目を表示しているのか分かるので、スタックの一番最初(タブ内に最初に表示される画面)だけタブバーを表示したい場合は以下のようにします。

stackNavigator.navigationOptions = ({ navigation }) => ({
  //スタックの一番最初(ScreenA1)だけタブバーを表示する
  tabBarVisible: navigation.state.index === 0,
});

全体

import {createStackNavigator, createBottomTabNavigator} from 'react-navigation'

const TabAStack =  createStackNavigator({
  ScreenA1,
  ScreenA2, //タブバー非表示にしたい
  ScreenA3, //タブバー非表示にしたい
});

TabAStack.navigationOptions = ({ navigation }) => ({
  //スタックの一番最初だけタブバーを表示する
  tabBarVisible: navigation.state.index === 0,
});

export default createBottomTabNavigator({
  TabAStack,
  TabB,
  TabC
});

画面の名前で切り分ける場合

stackNavigator.navigationOptions = ({ navigation }) => ({
  const { index } = navigation.state;
  // または、 const index = navigation.state.index;
  const { routeName } = navigation.state.routes[index];
  // または、 const  routeName  = navigation.state.routes[index].routeName;
  //ScreenA1でのみタブバーを表示する
  tabBarVisible = routeName === ScreenA1
});

(余談)navigation.stateの中身

上記の navigation.stateですが、
ScreenA1=>ScreenA2=>ScreenA3
と遷移した時の中身は以下のようになっています。

indexで現在表示している画面のindexが分かり、また routesの配列にはスタックの履歴(ScreenA1, ScreenA2, ScreenA3)が入っています。
そのため、routes[index]で、現在の画面の情報を取得できます。

{
"key":"TabAStack",
"isTransitioning":false,
"index":2, //スタックの何番目か
"routes":[
    {
    "routeName":"ScreenA1", //ScreenA1の画面名
    "key":"id-1536728255349-2"
    },
    {
    "params":{}, //遷移時にパラメータを指定するとその情報がここに入ります。ヘッダーのタイトルとか。
    "routeName":"ScreenA2", //ScreenA2の画面名
    "key":"id-1536728255349-9"
    },
    {
    "routeName":"ScreenA3", //ScreenA3の画面名
    "key":"id-1536728255349-10"
    }
    ],
"routeName":"TabAStack"}

ちなみに、一個前の画面(ScreenA2)に戻るとこうなります。

{
"key":"TabAStack",
"isTransitioning":false,
"index":1, //スタックの何番目か
"routes":[
    {
    "routeName":"ScreenA1", //ScreenA1の画面名
    "key":"id-1536728255349-2"
    },
    {
    "params":{}, //遷移時にパラメータを指定するとその情報がここに入ります。ヘッダーのタイトルとか。
    "routeName":"ScreenA2", //ScreenA2の画面名
    "key":"id-1536728255349-9"
    },
    ],
"routeName":"TabAStack"}

ScreenA3が破棄されたので、 routesからその情報が消えました。

(余談)各画面のnavigationOptionsで指定することはできない

これまでの例では、stackNavigator全体のnavigationOptionsでタブバーの表示/非表示を切り替えていましたが、 以下のように、stackNavigator全体ではなく各画面のnavigationOptionsを指定する方法では上手く行きません。

const TabAStack =  createStackNavigator({
  ScreenA1: {
    screen: ScreenA1,
    navigationOptions: { tabBarVisible: true },
  },
    ScreenA2: {
    screen: ScreenA2,
    navigationOptions: { tabBarVisible: false },//タブバー非表示にしたい
  },
    ScreenA3: {
    screen: ScreenA3,
    navigationOptions: { tabBarVisible: false },//タブバー非表示にしたい
  },
});

なぜなら、この時のnavigationOptionsはTabAStackにしかかかっておらず、bottomNavigatorの方にはかかっていないためです。

また、以下も上手く行きませんでした。

const TabAStack =  createStackNavigator({
  ScreenA1,
  ScreenA2, //タブバー非表示にしたい
  ScreenA3, //タブバー非表示にしたい
},{
navigationOptions : ({ navigation }) => ({
  //スタックの一番最初だけタブバーを表示する
  tabBarVisible: navigation.state.index === 0,
});
});

そもそも、タブ毎にstackNavigatorに突っ込まない方が良いらしい

この記事を書いている途中で気が付いたのですが、
React Navigationのこの箇所に、以下のようにありました

Another option here would be to add another stack navigator as a parent of the tab navigator, and put the details screen there. This is recommended.

タブバーのなかにstackNavigatorをつっこむのではなく、tabNavigatorとそれ以外のタブバー要らない画面をstackNavigatorにつっこむと。
そして、 This is recommended. とあります。

import {createStackNavigator, createBottomTabNavigator} from 'react-navigation'

const TabNavigator = createBottomTabNavigator({
  ScreenA1,
  TabB,
  TabC
});

export default createStackNavigator({
  TabNavigator,
  ScreenA2, //タブナビゲータの外なのでタブバー表示されない
  ScreenA3, //同上
})

確かに、スッキリしますね。

ただ、TabBやTabCにも同じようにタブバー非表示の導線がある場合、この方法だとごっちゃになる気がしました。

const TabNavigator = createBottomTabNavigator({
  ScreenA1,
  ScreenB1,
  ScreenC1,
});

export default createStackNavigator({
  TabNavigator,
  ScreenA2, 
  ScreenA3, 
  ScreenB2, 
  ScreenB3,
  ScreenC2, 
  ScreenC3,
})

こういう構成のアプリはあまり無いのかもしれないですが、個人的には、それぞれstackNavigatorで囲ってあげる方が、「ScreenA2とScreenA3はScreenA1からしか遷移しない」など体的な導線が分かりやすいのかなぁと思いました。

参考URL

以下を参考にしました。
- React Navigation - createBottomTabNavigator
- React Navigation - A tab navigator contains a stack and you want to hide the tab bar on specific screens
- Hide tabBar from stackNavigator

Why not register and get more from Qiita?
  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