5
votes

React-Navigation 3: Ouvrez modal avec createBottomTabNavigator et createStackNavigator

Je sais que cette question a déjà été posée, mais uniquement pour les anciennes versions de react-navigation. Depuis, certaines choses ont changé. createBottomTabNavigator rend beaucoup plus rapide la création d'un navigateur inférieur et la fonction jumpToIndex () n'existe plus.

Ma question est de savoir comment créer un Onglet inférieur de type Instagram, où les premier, deuxième, quatrième et cinquième boutons de navigation agissent comme les navigateurs d'onglets habituels et le bouton du milieu ( screen3 ) ouvre le modal screen3Modal . p>

Je l'ai essayé dans react-navigation 3.xx, en utilisant createBottomTabNavigator et createStackNavigator.

import React, { Component, } from 'react';
import { createBottomTabNavigator, createStackNavigator, createAppContainer, } from 'react-navigation';
import { Screen1, Screen2, Screen3, Screen4, Screen5 } from './screens';

const TabNavigator = createBottomTabNavigator({
  screen1: { screen: Screen1, },
  screen2: { screen: Screen2, },
  screen3: { 
    screen: () => null, 
    navigationOptions: () => ({
      tabBarOnPress: () => this.props.navigation.navigate('screen3Modal')
    })
  },
  screen4: { screen: Screen4, },
  screen5: { screen: Screen5, },
});

const StackNavigator = createStackNavigator({
  Home: { screen: TabNavigator },
  screen3Modal: { screen: Screen3, },
},
{
  initialRouteName: 'Home',
});

const StackNavigatorContainer = createAppContainer(StackNavigator);

export default class App extends Component {
  render() {
    return <StackNavigatorContainer />;
  }
}

Ce code crée la navigation par onglets et la navigation modale. Le modal peut être ouvert à partir d'un autre écran, mais il ne fonctionne pas à partir du navigateur d'onglets. J'obtiens le message d'erreur undefined n'est pas un objet (évaluation de '_this.props.navigation')


0 commentaires

5 Réponses :


2
votes

Vous pouvez tout envelopper dans le même StackNavigator, de cette façon, vous pouvez naviguer facilement vers d'autres itinéraires. Ici, je passe le screen3 comme route par défaut, mais vous pouvez le changer comme vous le souhaitez.

import React, { Component, } from 'react';
import { createBottomTabNavigator, createStackNavigator, createAppContainer, } from 'react-navigation';
import { Screen1, Screen2, Screen3, Screen4, Screen5 } from './screens';

const TabNavigator = createBottomTabNavigator({
  screen1: { screen: Screen1, },
  screen2: { screen: Screen2, },
  screen3: { screen: () => null, }, //this.props.navigation.navigate('screen3Modal')
  screen4: { screen: Screen4, },
  screen5: { screen: Screen5, },
});

const StackNavigator = createStackNavigator({
  Home: { screen: TabNavigator },
  screen3Modal: { screen: Screen3, },
},
{
  initialRouteName: 'screen3Modal',
});

const StackNavigatorContainer = createAppContainer(StackNavigator);

export default class App extends Component {
  render() {
    return <StackNavigatorContainer />;
  }
}


6 commentaires

Comment navigueriez-vous vers le modal? J'ai essayé tabBarOnPress: () => this.props.navigation.navigate ('screen3Modal') mais j'obtiens une erreur: undefined n'est pas un objet (évaluation de '_this.props.navigation ')


à partir de quel écran essayez-vous de naviguer? avez-vous votre constructeur (props) {super (props)} défini?


J'ai édité mon message ci-dessus. J'espère que c'est mieux compréhensible maintenant. L'écran initial doit être Accueil (le navigateur d'onglets) et lorsque screen3 est pressé, un modal doit s'ouvrir, qui couvre toute la page et masque le navigateur inférieur. non, je ne l'ai pas réglé. Tout ce que j'ai est dans le code ci-dessus.


Ah ok, n'était-ce pas clair avant, avez-vous vérifié cette réponse ici? stackoverflow.com/ questions / 54024006 /… Je pense que ce que vous essayez de faire n'est pas possible comme vous le faites


J'ai essayé une nouvelle solution: j'ai créé un nouvel écran dont le seul but est d'ouvrir le modal sur componentWillMount . maintenant je pourrais simplement ouvrir ce nouvel écran avec le navigateur d'onglets comme dans le lien que vous m'avez envoyé, mais cela crée quelques autres problèmes. Ne serait-il pas beaucoup plus propre d'utiliser tabBarOnPress et forceUpdate ce nouvel écran qui devrait alors au chargement ouvrir le modal? Si c'est possible, comment utiliser la fonction forceUpdate ? Je ne l'ai pas fait fonctionner.


J'ai trouvé une solution si simple. Je vais le poster plus tard



4
votes

J'ai trouvé une solution relativement simple: Cachez la barre de navigation d'origine avec display:"none"

<View style={{ flexDirection: "row", height: 50, justifyContent: "space-evenly", alignItems: "center", width: "100%" }}>
  <TouchableOpacity onPress={() => this.props.navigation.navigate("screen1")}><Text>1</Text></TouchableOpacity>
  <TouchableOpacity onPress={() => this.props.navigation.navigate("screen2")}><Text>2</Text></TouchableOpacity>
  <TouchableOpacity onPress={() => this.props.navigation.navigate("screen3")}><Text>3</Text></TouchableOpacity>
  <TouchableOpacity onPress={() => this.props.navigation.navigate("screen4")}><Text>4</Text></TouchableOpacity>
  <TouchableOpacity onPress={() => this.props.navigation.navigate("screen5")}><Text>5</Text></TouchableOpacity>
</View>

Et créez une nouvelle barre de navigation sur chaque écran

const TabNavigator = createBottomTabNavigator(
  {
    screen1: Screen1,
    screen2: Screen2,
    screen4: Screen4,
    screen5: Screen5,
  }, {
    tabBarOptions: {
      style: { display: "none", }
    }
  },
);

const StackNavigator = createStackNavigator(
  {
    Home: TabNavigator,
    screen3: Screen3
  }, {
    mode: 'modal',
  }
)

export default createAppContainer(StackNavigator);


2 commentaires

Solution solide comme le roc. Comment afficheriez-vous l'onglet actif? Dans votre exemple, il n'y a pas de concept d'onglet actif.


Ce n'est pas une bonne solution, vous êtes censé n'avoir qu'un seul composant de navigation par le bas dans toute l'application. L'ajout du même composant à tous les écrans fera scintiller la navigation et cela ne ressemblera plus à des «onglets».



0
votes

Eh bien, j'ai passé des heures et des heures à résoudre ce problème.

Voici une solution entièrement fonctionnelle et testée:

  1. Configurez votre conteneur d'applications en incluant la pile d'onglets et à ouvrir la pile de navigation modale:
  NavigationService.navigate(action.payload);
  1. Créez un conteneur d'application avec cette pile d'onglets par ceci a > guide

  2. Dans le TabNavigator dans le createBottomTabNavigator retournez le composant nul pour l'onglet spécifique (screen3) (pour désactiver la navigation par react-navigator) et gérez l'onglet manuellement dans defaultNavigationOptions en créant un composant personnalisé pour celui-ci.

  onPress = () => {
    this.props.dispatch({ type: 'NAVIGATION_NAVIGATE', payload: {
      key: 'screen3',
      routeName: 'screen3',
    }})
  }
  1. Gérez le clic manuellement dans un composant d'onglet personnalisé Screen3Tab avec TouchableWithoutFeedback & onPress. À l'intérieur du composant Screen3Tab:
  <TouchableWithoutFeedback onPress={this.onPress}>
    <Your custom tab component here />
  </TouchableWithoutFeedback>

  1. Une fois que vous avez détecté l'événement redux de répartition onPress
  const TabNavigator = createBottomTabNavigator({
    screen1: Screen1Navigator,
    screen2: Screen2Navigator,
    screen3: () => null,
    screen4: Screen4Navigator,
    screen5: Screen5Navigator,
}
    defaultNavigationOptions: ({ navigation }) => ({
      mode: 'modal',
      header: null,
      tabBarIcon: ({ focused }) => {
        const { routeName } = navigation.state;
        if (routeName === 'screen3') {
          return <Screen3Tab isFocused={focused} />;
        }
      },
    }),
  1. Gérez l'événement distribué à l'aide du Service de navigation . J'utilise redux-saga pour cela
  const FinalTabsStack = createStackNavigator(
    {
      tabs: TabNavigator,
      screen1: Screen1Navigator,
    }, {
      mode: 'modal',
    }
  )

Un peu compliqué mais ça marche.


0 commentaires

1
votes

J'ai trouvé une meilleure solution basée sur ce github problème . Il vous suffit d'ajouter dans la configuration de l'onglet spécifique, dans votre cas screen3 l'événement navigationOptions (vous l'aviez déjà), vous étiez assez proche, mais, vous deviez recevoir la navigation en paramètre, car il n'y a pas de contexte pour ceci pendant que vous l'utilisiez. Pour corriger le premier code que vous avez écrit, je le changerais en ceci et cela fonctionnerait:

navigationOptions: ({ navigation }) => ({
  tabBarOnPress: ({ navigation }) => {
    navigation.navigate("screen3Modal");
  }
})


0 commentaires

0
votes

ce qui peut être fait est d'empêcher l'action par défaut de l'événement de presse d'onglet et de naviguer vers l'écran souhaité avec l'animation souhaitée.

Dans mon cas, je voulais ouvrir un modal sous iOS dans un style de présentation qui provenait d'iOS 13. ce que j'ai fait a été d'empêcher l'action par défaut comme ci-dessous

    import { createStackNavigator, TransitionPresets } from '@react-navigation/stack'

'modelscreen' utilisé dans le code ci-dessus provient d'une pile de navigation qui ressemble à ci-dessous. Le navigateur d'onglets pour Tab.Screen dans le code ci-dessus est l'un des écrans de otherstacknavigation qui est utilisé dans la navigation ci-dessous.

const MainStack = ({ initialRoute }) => {
  return (
      <Stack.Navigator 
            initialRouteName="otherstacknavigation" 
            screenOptions={{
                headerShown: false,
                gestureEnabled: true,
                cardOverlayEnabled: true,
                ...TransitionPresets.ModalPresentationIOS
            }}>
        <Stack.Screen name='modalscreen' 
                      component={modalscreen}
                      options={{
                          headerShown: false
                      }} />
        <Stack.Screen name='otherstacknavigation' 
                      component={otherstacknavigation}
                      initialParams={{ initialRoute: initialRoute }}/>
      </Stack.Navigator>
  );
}

export default MainStack;

vous pouvez importer des TransitionPresets comme ci-dessous;

   <Tab.Screen name="screen1" 
    component={ EmptyScreen } // empty screen becuase I want nothing to present here so it was a component that returns null which is given bellow   
    listeners={({ navigation, route }) => ({
      tabPress: e => {
        e.preventDefault();
        navigation.navigate('modalscreen');
      },
    })} />


const EmptyScreen = () => {
  return null
}

Donc, dans mon cas, j'ai le premier navigateur de pile principale qui est MainStack ci-dessus. Il a un autre navigateur de pile qui est une autre navigation de pile au-dessus de MainStack. Cette autre navigation a un écran dont le composant est le navigateur d'onglets. et Tab. L'écran dans le code ci-dessus provient de ce navigateur d'onglets. Maintenant, à partir de l'un des onglets de ce navigateur d'onglets, je navigue vers 'modelscreen' qui se trouve dans MainStack. J'ai dû créer ce navigateur MainStack juste pour obtenir ces fonctionnalités, sinon mon code commencerait à partir d'une autre navigation. Parce que ModalPresentationIOS ne peut être appliqué qu'au niveau de la pile et non au niveau de l'écran.

J'utilise react-navigation-5. * Et je pense qu'il a répondu à plusieurs questions qui peuvent survenir dans une telle situation.


0 commentaires