How to lock orientation for specific screen on TabNavigator - react-native

I used TabNavigation for my app. In some screen, I want it only display on portrait orientation, and others are landscape. I've found react-native-orientation and try in my screens:
Screen 1
import React from "react";
import Orientation from 'react-native-orientation';
import { Text } from 'react-native'
export default Screen1 extends React.PureComponent{
componentDidMount(){
Orientation.lockToLandscape();
}
componentWillUnmount(){
Orientation.unlockAllOrientations();
}
render() {
return(<Text>Screen 1</Text>);
}
}
Screen 2
import React from "react";
import Orientation from 'react-native-orientation';
import { Text } from 'react-native'
export default Screen2 extends React.PureComponent{
componentDidMount(){
Orientation.lockToPortrait();
}
componentWillUnmount(){
Orientation.unlockAllOrientations();
}
render() {
return(<Text>Screen 2</Text>);
}
}
Tab Navigator
const AppNavigator = TabNavigator( {
Screen1: { screen: Screen1 },
Screen2: { screen: Screen2 },
});
But it always portrait, which mean that its orientation always set base on orientation of last screen I add in TabNavigator. Any help is appreciate, thank you in advance!
Edit 1
I've try StackNavigation instead, and it work. Still don't know why it's not run with TabNavigation.

It has been long time since I asked this questions, and react-navigation is upgrade to version 3. My solution for this problem is set orientation for tab each time it's pressed, cause tab screen isn't unmount when you click on other tab. To fix that, you can add tabBarOnPress to your navigationOptions of your screen like this
import Orientation from 'react-native-orientation';
...
class TabbarScreen1 extends Component {
static navigationOptions = {
...,
tabBarOnPress: ({ defaultHandler }) => {
// Orientation.lockToLandscape();
Orientation.lockToPortrait();
defaultHandler();
},
}
...
}

Related

React Native Open Tab Bar in Modal (using expo)

I'm trying to have one of the tab bar items open as modal when clicked, I'm currently using expo. I've read this: How do i make a TabNavigator button push a modal screen with React Navigation. However, I'm still learning React Native and I'm honestly not sure how to use this using expo navigation. Currently, I have created a stack navigator using "createStackNavigator" for each of the screens. And lastly, I have exported a bottom tab navigator including all of the stacks:
export default createBottomTabNavigator({
Tab1Stack,
Tab2Stack,
Tab3Stack,
Tab4Stack
});
I need Tab4 to open as a modal. Can someone people help me with this? Thank you in advance!!
Note this was built for "react-navigation": "3.3.0" so your mileage my vary for more recent versions of Expo and react-navigation.
To make a modal appear when you tap on the last tab in a TabNavigator requires you to nest your TabNavigator inside a StackNavigator.
So we could set up something like this:
#App.js
A simple App.js.
import React from 'react';
import AppContainer from './MainNavigation';
export default class App extends React.Component {
constructor (props) {
super(props);
this.state = {
};
}
render () {
return (
<AppContainer />
);
}
}
#MainNavigation.js
This file contains two navigators. A TabNavigator and a StackNavigator. The TabNavigator is nested inside the StackNavigator.
To be able to show the ModalScreen we have to override the tabBarOnPress function inside the defaultNavigationOptions which is inside the config for the TabNavigator.
We need to check the navigation.state.key to see where we are navigating too. If we are going to Tab3 we can intercept the call and navigate to the ModalScreen instead. Otherwise we use the defaultHandler and go to the tab that was tapped.
import Screen1 from './Screen1';
import Screen2 from './Screen2';
import Screen3 from './Screen3';
import ModalScreen from './ModalScreen';
import { createBottomTabNavigator, createAppContainer, createStackNavigator } from 'react-navigation';
const screens = {
Tab1: {
screen: Screen1
},
Tab2: {
screen: Screen2
},
Tab3: {
screen: Screen3
}
};
const config = {
headerMode: 'none',
initialRouteName: 'Tab1',
defaultNavigationOptions: {
tabBarOnPress: (data) => {
// this is where the magic happens
const { navigation, defaultHandler } = data;
// we check to see if the navigation key is going to be on Tab3
if (navigation.state.key === 'Tab3') {
// if it is we show the ModalScreen by navigating to it
navigation.navigate('ModalScreen');
} else {
// otherwise we call the defaultHandler and navigate to the tab we pressed
defaultHandler(navigation.state.key);
}
}
}
};
const TabNavigator = createBottomTabNavigator(screens, config);
const stackScreens = {
Tabs: {
screen: TabNavigator
},
ModalScreen: {
screen: ModalScreen
}
};
//we need to set the mode to be modal
const stackConfig = {
headerMode: 'none',
initialRouteName: 'Tabs',
mode: 'modal'
};
const MainNavigator = createStackNavigator(stackScreens, stackConfig);
export default createAppContainer(MainNavigator);
#Screen.js
A simple screen for each tab
import React from 'react';
import { View, StyleSheet, Text } from 'react-native';
export default class Screen extends React.Component {
render () {
return (
<View style={styles.container}>
<Text>Tab Screen</Text>
</View>
);
}
}
#ModalScreen
This screen is the modal that will appear when the tab for the third screen is tapped.
As it is part of the StackNavigator, defined above, it has access to the navigation prop. We set up a simple button that when pressed calls this.props.navigation.goBack() This will dismiss the modal.
import React from 'react';
import { View, StyleSheet, Text, Button } from 'react-native';
export default class Screen extends React.Component {
render () {
return (
<View style={styles.container}>
<Text>Modal Screen</Text>
<Button
title={'close modal'}
onPress={() => this.props.navigation.goBack()}
/>
</View>
);
}
}
Here is a snack with it working, https://snack.expo.io/#andypandy/show-modal-on-tab-press, hopefully it will show you how to set it up.

React-native with react-navigation: header not showing in screen

I do have a simple React-native app, using react-navigation for navigation. My issue is that for one of the screens the header and title is not showing, even though it is set in navigationOptions:
main.js:
import React from "react";
import { View, Text } from "react-native";
import {
createDrawerNavigator,
createAppContainer,
createStackNavigator
} from "react-navigation";
import ArticleList from "./screens/ArticleList";
import ArticleList2 from "./screens/ArticeList2";
import ArticleWebView from "./screens/ArticleWebView";
// create the Navigator to be used
const Stack = createStackNavigator({
ArticleList: { screen: ArticleList },
ArticleWebView: { screen: ArticleWebView }
});
const Drawer = createDrawerNavigator(
{
Stack: { screen: Stack },
ArticleList2: { screen: ArticleList2 }
},
{
initialRouteName: "Stack"
}
);
// The container for the navigator
const AppContainer = createAppContainer(Drawer);
class App extends React.Component {
render() {
return <AppContainer />;
}
}
export default App;
The ArticleList2 is a very simple component:
import React from "react";
import { View, Text } from "react-native";
import ArticleListComponent from "../components/ArticleListComponent";
class ArticleList2 extends React.Component {
static navigationOptions = {
title: 'Link2'
};
render() {
return (
<View>
<Text>LIst2</Text>
</View>
);
}
}
export default ArticleList2;
However, the title and header are not rendered in the app. Could anybody help please?
DrawerNavigator does not include a header. In order to create a header inside of ArticleList2 you will have to create a new StackNavigator for the component.
You have to think about and plan out the navigation flow of your app.
The Drawer section of the React Navigation documentation is a little lacking, but you can use the same principle as described in the Tab section
A stack navigator for each tab
const ArticleList2Stack = createStackNavigator({
ArticleList2: { screen: ArticleList2 }
});
const Drawer = createDrawerNavigator(
{
Stack: { screen: Stack },
ArticleList2: { screen: ArticleList2Stack }
},
{
initialRouteName: "Stack"
}
);

React Navigation: Is it possible to goBack() or pop() with params?

I'm using React Navigation (V2) and I have a basic set up of screens like this:
import {
createDrawerNavigator,
createStackNavigator,
createSwitchNavigator
} from "react-navigation";
import DetailScreen from "../screens/DetailScreen";
import HomeScreen from "../screens/HomeScreen";
import LoginScreen from "../screens/LoginScreen";
import SettingsScreen from "../screens/SettingsScreen";
const StackNavigator = createStackNavigator({
Home: { screen: HomeScreen },
Detail: { screen: DetailScreen }
});
StackNavigator.navigationOptions = ({ navigation }) => {
let drawerLockMode = "unlocked";
if (navigation.state.index > 0) {
drawerLockMode = "locked-closed";
}
return {
drawerLockMode
};
};
const DrawerNavigator = createDrawerNavigator({
HomeStack: { screen: StackNavigator },
Settings: { screen: SettingsScreen }
});
const Navigator = createSwitchNavigator(
{ LoginScreen, DrawerNavigator },
{ initialRouteName: "LoginScreen" }
);
export default Navigator;
While using my app the user ends up on the DetailScreen, makes some choices and is then supposed to go back to the HomeScreen. I want to pass params to the HomeScreen while going back. Unfortunately it seems like pop() and goBack() do not accept any params.
How can I go back a screen and pass params while doing so?
You can use the listeners for the screen and fire your refresh changes there.
didFocus - the screen focused (if there was a transition, the transition
completed)
// In your component
componentDidMount () {
this._onFocusListener = this.props.navigation.addListener('didFocus', (payload) => {
// Update the component (API calls here)
});
}

How change screen dynamically using Drawer Navigator

I have a Drawer Navigator with 2 screens, Home and Profile. What I need is, when I click in a TouchaleOpacity, change the screen from home to profile, how can I do this?
import { DrawerNavigator } from 'react-navigation';
import JsHome from './JsHome'
import JsProfile from './JsProfile'
const DrawerExample = DrawerNavigator({
JsHome : {
screen: JsHome
},
JsProfile : {
screen: JsProfile
},
}
PS: the TouchableOpacity are in another js file
each screen has navigation props. use this.props.navigation.navigate('JsProfile') in your touchable opacity

Disable rotation for specific views in react native

How can I disable rotation only for specific views (e.g: when using Navigator) and not for the entire app?
The question here already addresses disabling rotation for the entire app
With the react-native-orientation package, it's possible to lock the orientation to portrait/landscape. The package and documentation can be found here: https://github.com/yamill/react-native-orientation
Remember; you should not put your locking inside the rendering of scenes (nor the renderScene method). Since the Navigator re-renders all the scenes in the route stack, this would probably cause weird side effects for you. Rather, the locking/unlocking should be put in the code that interacts with the route stack (ie. calls the push/pop methods).
If your case is about more specific control over orientations of different screens in StackNavigator (something like Portrait -> LandscapeLeft -> LandscapeRight -> Portrait, and all the way back), here is a may-not-that-pretty solution:
packages needed: react-navigation, react-native-orientation;
define base screens as follow:
// baseScreen.js
import React, { Component } from "react";
import Orientation from "react-native-orientation";
export class PortraitScreen extends Component {
constructor(props) {
super(props);
this._willFocusSubscription = this.props.navigation.addListener("willFocus", payload => {
// lock to portrait when this screen is about to appear
Orientation.lockToPortrait();
})
}
componentWillUnmount() {
// remove subscription when unmount
this._willFocusSubscription.remove();
}
}
export class LandscapeScreen extends Component {
constructor(props) {
super(props);
this._willFocusSubscription = this.props.navigation.addListener("willFocus", payload => {
// lock to landscape
Orientation.lockToLandscape();
})
}
componentWillUnmount() {
// remove subscription either
this._willFocusSubscription.remove();
}
}
define concrete screens which extends abovementioned base screen(s):
// moduleScreens.js
import React from "react";
import { Button, View } from "react-native";
import { PortraitScreen, LandscapeScreen } from "/path/to/baseScreen";
export class VideoDescScreen extends PortraitScreen {
render() {
return (
<View>
<Button
title="watch video"
onPress={() => this.props.navigation.navigate("VideoPlayer")}
/>
</View>
)
}
}
export class VideoPlayerScreen extends LandscapeScreen {
render() {
return <View>...</View>
}
}
create route like this:
// route.js
import React from "react";
import { createStackNavigator } from "react-navigation";
import { VideoDescScreen, VideoPlayerScreen } from "/path/to/moduleScreens";
const stack = createStackNavigator(
{
VideoDesc: {
screen: VideoDescScreen
},
VideoPlayer: {
screen: VideoPlayerScreen
}
}
)
How it works? According to doc, we observe event willFocus when screen is initialized, and each time this screen is about to appear (focused) in navigation, we lock device to our desired orientation, works for both PUSH(to) and POP(back from) behaviors.
Hope it helps.