react-navigation.github.io icon indicating copy to clipboard operation
react-navigation.github.io copied to clipboard

Open a modal from a tab bar

Open brentvatne opened this issue 7 years ago • 15 comments

Moved over from https://github.com/react-navigation/react-navigation/issues/3003 @spencercarli came up with this solution: https://snack.expo.io/SyJKMkFUM We might need to change the API a bit to make it cleaner but we can document this approach for now!


  • If you're suggesting a change/improvement, tell us how it should work

It would be great to get documentation done for this "open modal from tab bar" layout. I've seen it asked a bunch of times in different places as it's a very common/popular requirement.

While not as helpful as writing documentation myself, here are some of the places this was asked, to show it would be useful to have it in the official docs:

  • https://stackoverflow.com/questions/46725173/react-navigation-tabnavigator-show-modal
  • https://stackoverflow.com/questions/42731093/how-to-push-a-modal-screen-using-reactnavigation
  • https://stackoverflow.com/questions/45520898/react-navigation-and-modal-tab-item
  • https://stackoverflow.com/questions/46725173/react-navigation-tabnavigator-show-modal
  • https://stackoverflow.com/questions/46106117/tabnavigator-with-a-modal-popup-with-react-navigation-like-instagram
  • https://stackoverflow.com/questions/42398911/how-do-i-make-a-tabnavigator-button-push-a-modal-screen-with-react-navigation
  • https://github.com/react-community/react-navigation/pull/525
  • https://github.com/react-community/react-navigation/issues/314
  • https://github.com/react-community/react-navigation/issues/1059
  • https://github.com/react-community/react-navigation/issues/1576
  • https://github.com/react-community/react-navigation/issues/2259
  • https://github.com/react-community/react-navigation/issues/1734
  • https://github.com/react-community/react-navigation/issues/770

@TomAtterton seems to have a solution in this issue

https://github.com/react-community/react-navigation/issues/1576

He also left the same comment here:

https://stackoverflow.com/questions/42398911/how-do-i-make-a-tabnavigator-button-push-a-modal-screen-with-react-navigation

Anyway, best practice or not, Tom seems to have the only solution? Maybe he could give a slightly longer form answer and that can be used for docs? @spencercarli

I will try to write something up potentially.

brentvatne avatar Feb 07 '18 20:02 brentvatne

Just for the record, the answer by Thomas Kekeisen worked best: https://stackoverflow.com/a/42907868/961886

iosdev-republicofapps avatar May 08 '18 01:05 iosdev-republicofapps

FYI with v2 and "react-navigation-tabs" version "0.3.0" I have come up with a simpler solution for doing this: https://github.com/react-navigation/react-navigation/issues/1576#issuecomment-392918395

sean-macfarlane avatar May 29 '18 19:05 sean-macfarlane

My goal was to create a pop up modal similar to what the OP was looking for in #1059 but I found that almost all of the solutions referenced involved a full page modal that covered up the tab bar and was not really an overlay, which was not what I was looking for.

The way I got it to work was to create a custom tab bar component and render the modal through there.

export default createBottomTabNavigator({
  ...
  AddStack,
  ...
}, {
  tabBarComponent: TabBarComponent,
})

where AddStack is just an empty StackNavigator with screen: () => null.

And in my TabBarComponent.js,

export default class TabBarComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      modalVisible: false
    }
  }

  render() {
    const { navigation, renderIcon, activeTintColor, inactiveTintColor } = this.props;
    const { routes } = navigation.state;

    return (
      <SafeAreaView style={styles.tabbar}>
        {routes && routes.map((route, index) => {
          const focused = index === navigation.state.index;
          const tintColor = focused ? activeTintColor : inactiveTintColor;
          return (
            <TouchableWithoutFeedback
              key={route.key}
              style={styles.tab}
              onPress={() => {
                if (route.key == "AddStack") {
                  this.setState({ modalVisible: true })
                } else {
                  navigation.navigate(route)
                }
              }}
            >
              <View style={styles.tab}>
                { renderIcon({route, index, focused, tintColor })}
                <Modal
                  isVisible={this.state.modalVisible}
                  animationIn="fadeInUp"
                  animationOut="fadeOutDown"
                  backdropOpacity={.5}
                  onBackdropPress={() => this.setState({ modalVisible: false })}
                  onBackButtonPress={() => this.setState({ modalVisible: false })}
                  style={styles.bottomModal}
                  useNativeDriver={true}
                >
                  <View style={styles.modal}>
                     // Modal Content
                  </View>
                </Modal>
              </View>
            </TouchableWithoutFeedback>
          );
        })}
      </SafeAreaView>
    );
  }
};

bjjeong avatar Dec 18 '18 21:12 bjjeong

class TabBarButtonComponent extends Component { goToListPage = () => { this.props.children[0].props.navigation.navigate("ListPage"); }; }

this.props.children[0].props.navigation.navigate("ListPage");

就这一个,查看props得来的

zhuxx2021 avatar Jan 03 '19 02:01 zhuxx2021

mr.@bjjeong i try to make adding post like you did, but its always show me the modal view this the code

export default class TabBarComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      modalVisible: false
    }
  }

  render() {
    const { navigation, renderIcon, activeTintColor, inactiveTintColor } = this.props;
    const { routes } = navigation.state;

    return (
      <SafeAreaView style={{flexDirection: 'row',
                height: 50,
                width: '100%',}}>
        {routes && routes.map((route, index) => {
          const focused = index === navigation.state.index;
          const tintColor = focused ? activeTintColor : inactiveTintColor;
          return (
            <TouchableWithoutFeedback
              key={route.key}
              style={{flex:1}}
              onPress={() => {
                if (route.key == "Adding") {
                  console.log(route.key);
                  this.setState({ modalVisible: false })
                } else {
                  navigation.navigate(route)
                }
              }}
            >
              <View style={{flex:1}}>
                { renderIcon({route, index, focused, tintColor })}
                <Modal
                  isVisible={false}
                  animationIn="fadeInUp"
                  animationOut="fadeOutDown"
                  backdropOpacity={.5}
                  onBackdropPress={() => this.setState({ modalVisible: false })}
                  onBackButtonPress={() => this.setState({ modalVisible: false })}
                  style={{flex:1}}
                  useNativeDriver={true}
                >
                  <View style={{flex:1, backgroundColor:'rgba(100,100,100, 0.5)'}}>
                     <Text style={{color:"red"}}> modal content</Text>
                  </View>
                </Modal>
              </View>
            </TouchableWithoutFeedback>
          );
        })}
      </SafeAreaView>
    );
  }
};

can you tell me whats wrong with that code?

jemjov41 avatar May 15 '19 07:05 jemjov41

https://github.com/brentvatne/react-navigation-workshop-examples/blob/d90687777a95599a32e395f9e16aae642f5a8ee5/example/sections/Nesting.js#L61-L82

you can do something like this to change the behavior of pressing a tab

brentvatne avatar May 16 '19 03:05 brentvatne

@setanallas you have 2 things wrong.

  1. The isVisible prop of your Modal is hardcoded to equal false. Instead, do this:
<Modal
  isVisible={this.state.isVisible}
</Modal>
  1. In your TouchableWithoutFeedback, you need to have the onPress change the state of the isVisible variable.
<TouchableWithoutFeedback
  onPress={() => {
    if (route.key === 'Adding') {
      this.setState({ isVisible: true })}
    } else {
      navigation.navigate(route)
    }
  }
>
</TouchableWithoutFeedback>

That should be it.

bjjeong avatar May 16 '19 03:05 bjjeong

@bjjeong yea i set it hardcode because its always showing even im not click the bottom tab button, even i set it to false its still show

but no problem i already fix it thx mr.@bjjeong for inspire me

jemjov41 avatar May 16 '19 04:05 jemjov41

What's the current way to do this in V5?

immortalx avatar Nov 22 '19 21:11 immortalx

Any update to do this in v5?

sperezm97 avatar Feb 14 '20 18:02 sperezm97

@immortalx @sperezm97

Wish there was an easier way, but this is how I achieved it in v5.

I passed in a custom tab bar component into the tab navigator. This will give you the ability alter what happens with the onPress.

bernardmma avatar Feb 18 '20 05:02 bernardmma

I found this helpful for open the modal from the navbar in react navigation 5: https://github.com/react-navigation/react-navigation/issues/7249

omorhefere avatar Mar 21 '20 19:03 omorhefere

@sperezm97 for opening a modal from tab click use listeners

      <Tab.Screen
        name="AddEntry"
        component={EntryFormScreen}
        listeners={({ navigation, route }) => ({
          tabPress: e => {
            e.preventDefault();
            navigation.navigate("EntryFormFlow");
          },
        })}
        options={({ route }) => ({
          tabBarIcon: ({ focused, color }) => (
            <AntDesign name={"pluscircleo"} size={25} color={color} />
          ),
        })}
      />

EdmundMai avatar Mar 28 '20 22:03 EdmundMai

@sperezm97 for opening a modal from tab click use listeners

      <Tab.Screen
        name="AddEntry"
        component={EntryFormScreen}
        listeners={({ navigation, route }) => ({
          tabPress: e => {
            e.preventDefault();
            navigation.navigate("EntryFormFlow");
          },
        })}
        options={({ route }) => ({
          tabBarIcon: ({ focused, color }) => (
            <AntDesign name={"pluscircleo"} size={25} color={color} />
          ),
        })}
      />

Dude thank you. I knew there had to be a simpler solution for v5. Just tried it out and works exactly as I needed.

kturcios avatar Apr 13 '20 01:04 kturcios

Any progress?

hengkx avatar Jun 25 '20 07:06 hengkx