I am in a situation within my project where it would be convenient to "prohibit the user from interacting with the app". I would like to do this, by rendering an invisible screen that is covering the entire display.
I notice that once the bottom tab is rendered, I cannot seem to force a view over it. Is there a way I can give my view priority over everything? I have tried zIndex but no luck, I just want my view to cover the entire screen.
I have an example demo here on snack expo where you can replicate my issue exactly. I have also included some code below that gives you the gist.
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="ScreenA" component={ScreenA} />
<Tab.Screen name="ScreenB" component={ScreenB} />
</Tab.Navigator>
</NavigationContainer>
);
}
export default function ScreenA() {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>
Screen A.
</Text>
<View style={{justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink', height: 500}}>
<Text style={styles.paragraph}>Is it possible to make this view cover the entire screen? Including the bottom tab and top?</Text>
</View>
</View>
);
}
After taking a look at this stackoverflow answer I've come up with a solution that hides/show the tab bar buttons base on the state of a variable:
const [showTabBar, setShowTabBar] = useState(false);
useEffect(() => {
props.navigation.setOptions({
tabBarStyle: { display: showTabBar ? 'flex' : 'none' },
});
}, [showTabBar, props.navigation]);
A demo
While it removes the tab bar entirely, it does ensure that the tab bar is non-interactable. If you find it necessary to have the tab bars visible, but unpressable, you could first create a custom tab bar component, add some variable that will make the buttons do nothing when active, use it in Tab.Navigator and then render it at the bottom of the screen in ScreenA when showTabBar is false (make sure to set the unpressable variable to true)
Related
I'm using Native Stack Navigator v6 and trying to add borderBottomRightRadius and borderBottomLeftRadius as shown below. It's working in Expo Web but not in iOS or Android, as shown in screenshot below.
I'd appreciate guidance on how to fix this, or if this is not the right approach, please suggest another way to achieve bottom rounded corners for the header bar.
<HomeStack.Screen
name="HomeScreen"
component={HomeScreen}
options= {{
headerTitle: "Home Screen",
headerStyle: {
backgroundColor: '#21ABA5',
borderBottomRightRadius: 20,
borderBottomLeftRadius: 20,
overflow: 'hidden',
background: 'transparent'
},
headerTitleStyle: {
color: '#fff'
},
headerTintColor: 'white',
headerTransparent: true
}}
/>
Let me edit my answer. If you don't want for web at all, you can create your own header. If you want to apply to all screens, add it to Stack.Navigator's ScreenOptions.
import { getHeaderTitle } from "#react-navigation/elements";
function StackScreen() {
return (
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
header: ({ navigation, route, options, back }) => {
const title = getHeaderTitle(options, route.name);
return (
<MyHeader
title={title}
leftButton={
back ? (
<MyBackButton onPress={navigation.goBack} />
) : undefined
}
style={options.headerStyle}
/>
);
},
}}
/>
</Stack.Navigator>
);
}
After further research turns out this is impossible.
Native Stack Navigator depends on native platform options which apparently don't support borderRadius out-of-the-box. The only option that can be affected via the headerStyle is backgroundColor.
Other options include using the Stack Navigator instead of the Native Stack Navigator, or building an entirely custom header component. However, the latter loses most of built-in the advantages of the Native Stack Navigator. Thus, I'll be switching to the JS-based Stack Navigator which is far more customizable.
I am trying to create my first React Native application. I have a login screen from which I want to navigate to a register screen if users want to sign up.
To achieve this, I was thinking of opening a modal above the first screen (login). I created the following:
import { NavigationContainer } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
const Stack = createStackNavigator();
function RegisterScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 30 }}>Register Screen</Text>
<Button onPress={() => navigation.goBack()} title="Go back" />
</View>
);
}
function MyStack() {
return (
<Stack.Navigator>
<Stack.Group>
<Stack.Screen name="Login" component={LoginScreen} options={{ headerShown: false }} />
</Stack.Group>
<Stack.Group screenOptions={{ presentation: "modal", headerShown: false }} options={{modalPresentationStyle: 'fullScreen'}}>
<Stack.Screen name="Register" component={RegisterScreen} />
</Stack.Group>
</Stack.Navigator>
)
}
export default function App() {
return (
<NavigationContainer>
<MyStack />
</NavigationContainer>
);
}
This works perfectly fine, the modal shows. However, as you can see in this screenshot below, the modal is shown with modalPresentationStyle .automatic (in iOS). I want it to show as .fullScreen, so the hierarchy is not visible in the screen. Basically, I want the view to be shown from the safeArea/statusBar all the way down to the safeArea on the bottom.
How can I achieve this?
The terminology in React Native isn't the same as terminology in native apps. If you want a full-screen screen, you probably don't want a modal. If you want a vertical animation, change the animation based on docs:
screenOptions={{ cardStyleInterpolator: CardStyleInterpolators. forVerticalIOS, headerShown: false }}
https://reactnavigation.org/docs/stack-navigator/#animation-related-options
There's also no modalPresentationStyle: 'fullScreen' in documentation and Group component doesn't take an options prop.
The accepted answer states:
If you want a full-screen screen, you probably don't want a modal.
That's not necessarily true. Use
screenOptions={{ presentation: 'transparentModal' }}
or
screenOptions={{ presentation: 'fullScreenModal' }}
for a full screen modal animation you can't dismiss on iOS.
The title is very confusing, but the explanation that I will give will be more clear.
I am creating one StackNavigator for my app, and I am defining one icon to be displayed on the header of one of my screens like so:
const navigator= createStackNavigator(
{
Initial: {
screen: Posts,
navigationOptions: {
title: "All Posts",
headerRight: () => {
return (
<TouchableOpacity style={{ padding: 5 }}>
<Feather name={"plus"} size={30} />
</TouchableOpacity>
);
},
},
},
Details: Details,
Create: Create,
},
{
initialRouteName: "Initial",
}
);
const App = createAppContainer(navigator);
export default () => {
return (
<Provider>
<App />
</Provider>
);
};
The problem is that I want to navigate the user to the Create screen when the user presses the icon that I am rendering on the right side of the header, but I do not know how to have access to the navigation.navigate() function that the navigator generates for all the screens. I know that I can define the header on the Posts' screen file, so I have access to the navigation.navigate() function, but I want to know if there is some way to do it on the App.js file.
EDIT
Reading the documentation I saw that the way that I am was creating the navigator is not what the official documentation recommends. I learned to make it like this by one old course, using React Navigation 4.x, and now with React Navigation 6.x I perceived the difference in the creation and changed the way that I was doing it on my app. You can check the documentation for the problem that I was having here
You can prepare your navigation option this way by sending props
options={(props) => ({
headerRight: () => <TouchableOpacity
onPress={()=>props.navigation.navigate('Screen')} style={{ padding: 5 }}>
<Feather name={"plus"} size={30} />
</TouchableOpacity>
})}
I am new to React Native. I am so confused about navigation stack.
Here is my code.
const AppNavigator = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Login" component={LoginScreen} options={{ title: 'login' }}/>
<Stack.Screen name="Home" component={HomeScreen} options={{ title: 'home' }}/>
</Stack.Navigator>
</NavigationContainer>
);
};
Users will see the login screen once they run this app.
And here is LogInScreen.
const LogInScreen = ({ navigation }) => {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="google login"
onPress={() =>
navigation.navigate('Home')
}
/>
</View>
);
};
Users is going to see home screen after they clicked login button.
But the problem is they still can see back button in home screen.
After log in , it's not necessary to show login screen to users.
Hence, I need to reset stack to remove back button in home screen.
If you want to remove the back button in the home screen, you can use the headerLeft option as specified here.
I think it is enough if you're developing for iOS. For Android, the user can still use the back button to go back. I think resetting the stack and navigate the user the home page is also a good solution. You can look into this post
I have created an app which uses the Navigator component, I'm wondering is there a way I can implement a header and footer component outside of the scene?
A screenshot of what I have currently:
http://i.stack.imgur.com/XHDKC.png
This first attempt was accomplished by making the header and footer a single component with absolute styles.
//index.js
<Navigator initialRoute={{id: 'home', title: window.title}}
renderScene={renderScene}
navigationBar={<DefaultHeader toggleSideMenu={this.toggleSideMenu}
route={route.id} />}/>
//DefaultHeader.js
<View style={styles.navContainer}>
<View style={styles.header}>
</View>
<View style={styles.footer} shouldUpdate={false}>
</View>
</View>
Although appeared to work, I was unable to click around anything within the scene due to the render order in React's Navigator component.
I decided to re-think my approach and fully separated navigation bars from the Navigator component. This relies on you passing down a routing function and any other route info.
routeTo: function (route) {
if (route.to == "back") {
this.refs.navigator.pop();
} else {
this.refs.navigator.push(route);
}
},
canGoBack: function () {
return this.refs.navigator && this.refs.navigator.getCurrentRoutes().length > 1
},
getDefaultRoute: function () {
return {id: 'home', title: window.title};
},
getCurrentRoute: function () {
if (this.refs.navigator) {
return _.last(this.props.navigator.getCurrentRoutes());
}
return this.getDefaultRoute();
},
render() {
return (
<View style={styles.container}>
<DefaultHeader routeTo={this.routeTo} route={this.getCurrentRoute()}
toggleSideMenu={this.toggleSideMenu}/>
<Navigator
ref="navigator"
initialRoute={this.getDefaultRoute()}
renderScene={renderScene}
/>
<DefaultFooter routeTo={this.routeTo} route={this.getCurrentRoute()}/>
</View>
)
}
Although it is pretty "hacky" - why don't you add a third view (expanding fully) between the header and footer and set onStartShouldSetResponder and onMoveShouldSetResponder to return false for both: the middle view and the navContainer view). See https://facebook.github.io/react-native/docs/gesture-responder-system.html. I am not sure if it will work but it might be worth trying.
The best way, however, would be to modify the Navigator component and add footer props and displaying there. It's pure javascript, so it should be fairly easy to do.
I am using RN 0.36 and I was able to workaround this by using the navigator height to margin the footer:
<View style={{flex: 1}}>
<ScrollView>
...
</ScrollView>
<View style={{
height: 40,
borderTopWidth: 1,
borderTopColor: colors.grey,
flex: 0,
marginBottom: Navigator.NavigationBar.Styles.General.TotalNavHeight
}}>
<Text>Footer</Text>
</View>
</View>
where my index files (ie index.ios.js) looks like
<NavigatorIOS
style={{flex: 1}}
initialRoute={{
title: ' ',
component: Main
}}
...
Check NavigatorIOS and Navigator