m_shige1979のときどきITブログ

プログラムの勉強をしながら学習したことや経験したことをぼそぼそと書いていきます

Github(変なおっさんの顔でるので気をつけてね)

https://github.com/mshige1979

react-nativeでタブナビゲーションを実装

作成したやつ


react-navigationのTopTabsサンプル

参考情報

React Navigationのタブナビゲーションをカスタマイズしてみよう! - bagelee(ベーグリー) createMaterialTopTabNavigator | React Navigation

github

react-native_samples/sampleAppNavigation01 at main · mshige1979/react-native_samples · GitHub

コード全体

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

import React, {useState} from 'react';
import {View, StyleSheet, StatusBar, Platform} from 'react-native';

import {NavigationContainer} from '@react-navigation/native';
import {createMaterialTopTabNavigator} from '@react-navigation/material-top-tabs';

// タブエリア
import AppTabBar from './src/components/AppTabBar';

// サンプルページ
import Home from './src/screens/home';
import Profile from './src/screens/profile';
import MyPage from './src/screens/mypage';

// タブコンポーネント
const TopTabs = createMaterialTopTabNavigator();
const TopTabsScreen = () => {
  const [swipeEnabled, setSwipeEnabled] = useState(true);

  // Profileの場合は左右にスワイプする
  // Profile以外の場合はスワイプしない
  // 参考:https://stackoverflow.com/questions/63611393/react-native-material-top-tab-navigator-swipe-disable-depending-on-screens
  const focusCheck = ({navigation, route}) => ({
    focus: () => {
      console.log('focus: ', route.name);
      setSwipeEnabled(route.name === 'Profile');
    },
  });

  return (
    <TopTabs.Navigator
      swipeEnabled={swipeEnabled} // スワイプによる移動を制御(true: スワイプ移動、false: 移動しない)
      tabBar={(props) => <AppTabBar {...props} />}
      style={{
        marginTop: Platform.select({
          ios: 50,
          android: 0,
        }),
      }}>
      <TopTabs.Screen name="Home" component={Home} listeners={focusCheck} />
      <TopTabs.Screen
        name="Profile"
        component={Profile}
        listeners={focusCheck}
      />
      <TopTabs.Screen name="MyPage" component={MyPage} listeners={focusCheck} />
    </TopTabs.Navigator>
  );
};

// メイン
const App = () => {
  return (
    <>
      <NavigationContainer>
        <TopTabsScreen />
      </NavigationContainer>
      <StatusBar barStyle="dark-content" />
    </>
  );
};

const styles = StyleSheet.create({});

export default App;

1.画面を作成

import React, {useEffect} from 'react';
import {View, Text, StyleSheet, Button} from 'react-native';

const Home = ({navigation, route}) => {
  return (
    <View style={styles.container}>
      <View>
        <Text>Home</Text>
      </View>
      <View
        style={{
          margin: 10,
        }}>
        <Text>Topタブのサンプル このタブはスワイプしない</Text>
      </View>
      <View>
        <Button
          title="Profileへ移動"
          onPress={() => {
            navigation.jumpTo('Profile');
          }}
        />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
});

export default Home;

こーいうものを作成する

2. タブコンポーネントを作成

1で作成したコンポーネントを設定する。 基本2〜5位がちょうどいい感じ

// タブコンポーネント
const TopTabs = createMaterialTopTabNavigator();
const TopTabsScreen = () => {
  const [swipeEnabled, setSwipeEnabled] = useState(true);

  // Profileの場合は左右にスワイプする
  // Profile以外の場合はスワイプしない
  // 参考:https://stackoverflow.com/questions/63611393/react-native-material-top-tab-navigator-swipe-disable-depending-on-screens
  const focusCheck = ({navigation, route}) => ({
    focus: () => {
      console.log('focus: ', route.name);
      setSwipeEnabled(route.name === 'Profile');
    },
  });

  return (
    <TopTabs.Navigator
      swipeEnabled={swipeEnabled} // スワイプによる移動を制御(true: スワイプ移動、false: 移動しない)
      tabBar={(props) => <AppTabBar {...props} />}
      style={{
        marginTop: Platform.select({
          ios: 50,
          android: 0,
        }),
      }}>
      <TopTabs.Screen name="Home" component={Home} listeners={focusCheck} />
      <TopTabs.Screen
        name="Profile"
        component={Profile}
        listeners={focusCheck}
      />
      <TopTabs.Screen name="MyPage" component={MyPage} listeners={focusCheck} />
    </TopTabs.Navigator>
  );
};

swipeEnabled

⇨ 左右にスワイプするための設定値、trueでスワイプする、falseでしない

フォーカスイベントで任意の画面のみスワイプするように制御可能

  // Profileの場合は左右にスワイプする
  // Profile以外の場合はスワイプしない
  // 参考:https://stackoverflow.com/questions/63611393/react-native-material-top-tab-navigator-swipe-disable-depending-on-screens
  const focusCheck = ({navigation, route}) => ({
    focus: () => {
      console.log('focus: ', route.name);
      setSwipeEnabled(route.name === 'Profile');
    },
  });

3.メイン画面に組み込む

// メイン
const App = () => {
  return (
    <>
      <NavigationContainer>
        <TopTabsScreen />
      </NavigationContainer>
      <StatusBar barStyle="dark-content" />
    </>
  );
};

おまけ

独自タブバー

標準でスタイルが適用されているが、一部独自に組み込みたい場合

import React from 'react';
import {View, Text, TouchableOpacity} from 'react-native';
import Animated from 'react-native-reanimated';

// 独自タブ
// 参考:https://bagelee.com/programming/react-native/react-navigation-customize/
// 参考:https://reactnavigation.org/docs/material-top-tab-navigator
const AppTabBar = ({state, descriptors, navigation, position}) => {
  const {routes, index} = state;

  const {
    containerStyle,
    tabStyle,
    selectedTabStyle,
    textStyle,
    selectedTextStyle,
  } = styles;

  //console.log(position);
  //console.log(descriptors);

  return (
    <View style={containerStyle}>
      {routes.map((route, idx) => {
        //const inputRange = state.routes.map((_, i) => i);
        //console.log(inputRange);

        // 選択しているタブ
        if (index === idx) {
          return (
            <View key={idx} style={[tabStyle, selectedTabStyle]}>
              <Text style={[textStyle, selectedTextStyle]}>
                {routes[idx].name}
              </Text>
            </View>
          );
        }

        // 他のタブ
        return (
          <TouchableOpacity
            style={tabStyle}
            key={idx}
            onPress={() => {
              // タップしたら他のタブへ切り替え
              navigation.navigate(route.name);
            }}>
            <Animated.Text style={[textStyle]}>
              {routes[idx].name}
            </Animated.Text>
          </TouchableOpacity>
        );
      })}
    </View>
  );
};

const styles = {
  containerStyle: {
    //paddingTop: 30,
    borderBottomWidth: 3,
    borderBottomColor: '#5ab4bd',
    display: 'flex',
    flexDirection: 'row',
    justifyContent: 'space-between',
    backgroundColor: '#fcf6d6',
  },
  tabStyle: {
    flex: 1,
    //marginRight: 1,
    //marginLeft: 1,
    height: 40,
    //borderTopLeftRadius: 16,
    //borderTopRightRadius: 16,
    backgroundColor: '#ffffff',
  },
  selectedTabStyle: {
    backgroundColor: '#5ab4bd',
  },
  textStyle: {
    fontWeight: 'bold',
    textAlign: 'center',
    paddingTop: 14,
  },
  selectedTextStyle: {
    color: '#ffffff',
  },
};

export default AppTabBar;

とりあえず、ここまでにしておく・・・