im using react navigation 5 on my project, and somewhere in the project i need to make nested navigators like below:
*Stack Navigator
**Material top tab navigatior
***Material bottom tab navigator
**** Stack Navigator
My goal is, actually i want to have 3 static screens like instagram. On the left side will be Camera, then in the center actual content and in the right side something else. And only when you are in center i want to bottom tabs are visible thats why i created it like that. In this case in all the screens you can swipe right or left for reaching to camera or right component.
But what i want to do is, i want do "disable" swipe action in detail screens in center component. Becose center component which is material bottom tab navigator above, contains some stack navigators also, like posts, postDetail. and when i go to postDetail i want to disable swipe left or right action. Becouse in some details im using some swipeable elements like react native swiper etc.
I have tried to give gesturesEnabled: false , swipeEnabled: false in material bottom navigator as props but it doesn't work becouse it's a bottom tab navigator and doesn't take these params.
I also tried to catch state index and if its greater than 0 or 1 i would disable it but in material top tab navigator doesn't change the index when i go to postDetail for example. It's not working like previous version which is react navigation 4.
const BlogStack = createMaterialTopTabNavigator();
const BlogStackNavigator = ({ navigation, route }) => {
return (
<BlogStack.Navigator
initialRouteName="Blogs"
tabBarOptions={{
style: {
height: 0,
},
}}
//swipeEnabled={route.state && route.state.index > 0 ? false : true}
>
<BlogStack.Screen name="Camera" component={Camera} />
<BlogStack.Screen name="Blogs" component={Blog} />
<BlogStack.Screen name="Timeline" component={Profile} />
</BlogStack.Navigator>
);
};
Try setting gestureEnabled to false in Screen's options.
<Screen name={key} options={{gestureEnabled: false}} component={Component} />
You need to set gestureEnabled to false but it's not sent as a prop. You need to set it in the options prop. If you want to set it for all screens you can place it in the navigator like this:
<BlogStack.Navigator
initialRouteName="Blogs"
tabBarOptions={{
style: {
height: 0,
},
}}
screenOptions={{gestureEnabled: false}}
>
or for only specific screens:
<BlogStack.Screen name="Camera" options={{gestureEnabled: false}} component={Camera} />
Related
I have a problem during the logout in my react native app. I set many navigators to handle multiple navigation flow, but when I try to logout from the app, the login screen appears two times.
I try to explain how I organize my navigators below:
I created a TabNavigator which handle the main navigation of the app if a user is logged in otherwise I show the LoginNavigator which handle login, registration and password forgot screens.
I use a state, stored on redux for handling the login state (isLoggedIn).
<NavigationContainer>
{isLoggedIn ? <TabNavigator/> : <LoginNavigator/>}
</NavigationContainer>
Into the TabNavigator I set other navigator for handling page flow in each sections, like the exemple below:
TabNavigator
Screen X
Navigator1
Screen A
Screen B
Navigator2
Screen C
Screen D
Screen Y
Into the Screen C I'd like to logout from the app, so I call an action which set the isLoggedIn state to false.
What happens is that the Login screen (the inital screen of LoginNavigator) appears, but it suddently slides off and another Login screen appears.
It happens only from nested screen, instead if I try to logout from Screen X or Screen Y it works perfectly fine.
Does anyone encounter this problem? How can I solve this behaviour?
I solved this behaviour!
Instead of showing the correct navigator using a ternary operation, base on redux state, I wrap TabNavigator and LoginNavigator inside a custom stack navigator with a WaitingScreen.
I set the WaitingScreen as inital route and I moved the logic about which route between MainNavigator and LoginNavigator has to be shown inside WaitingScreen, in a useLayouEffect with isLoggedIn as dependecy.
<NavigationContainer>
<Stack.Navigator
initialRouteName={NavConst.LOADING}
screenOptions={{
headerShown: false,
contentStyle: {
backgroundColor: Color.primary600,
},
animation: "none",
}}
>
<Stack.Screen name={NavConst.LOADING} component={WaitingScreen} />
<Stack.Screen name={NavConst.LOGIN_NAV} component={LoginNavigator} />
<Stack.Screen name={NavConst.MAIN_NAV} component={MainNavigator} />
</Stack.Navigator>
</NavigationContainer>
and inside the WaitingScreen:
useLayoutEffect(() => {
isLoggedIn
? navigation.navigate(NavConst.MAIN_NAV);
: navigation.navigate(NavConst.LOGIN_NAV);
}, [isLoggedIn]);
I can't seem to figure out what is causing this code to not work on Android. It works well on iOS. It is a custom tab bar which lives inside a screen of the "normal" bottom tab bar. It has three screens and when switching between them I can see that things are happening under the hood, it's just that only the last tab screen is shown. All of the screens are being rendered and using useFocusEffect I can see that the screens are getting focused when the buttons are pressed, but they are not shown. So it's like they´re hidden behind the last tab screen.
The code is pretty much exactly taken from the example in the react-navigation guide (and I have also tried this with the exact example code):
https://reactnavigation.org/docs/custom-navigators
except that the top level <view>s had to switch places for the tab bar to be visible.
The tab navigator is then embedded like this (I have just replaced the actual names with numbers):
const 4Tab = createCustomTabNavigator<4ParamList>()
export default function 4TabNavigator() {
return (
<4Tab.Navigator initialRouteName="1">
<4Tab.Screen name="1" component={1Navigator} />
<4Tab.Screen name="2" component={2Navigator} />
<4Tab.Screen name="3" component={3Navigator} />
</4Tab.Navigator>
)
}
which in turn is embedded like so (4TabNavigator):
const Tab = createBottomTabNavigator() // Normal tab bar
const TabStack: React.FC<Props> = props => {
return (
<Tab.Navigator
initialRouteName={TabRouteName.1}
screenOptions={tabScreenOptions}>
<Tab.Screen name={TabRouteName.1} component={1Navigator} />
<Tab.Screen name={TabRouteName.2} component={2Navigator} />
<Tab.Screen name={TabRouteName.3} component={3Stack} />
<Tab.Screen
name={TabRouteName.4}
component={4TabNavigator}
options={{ lazy: true }}
/>
<Tab.Screen name={TabRouteName.5} component={5Navigator} />
</Tab.Navigator>
)
}
I'm not sure if this is an issue with react-navigation at all, but I also don't know what else could be wrong. I have looked through react-navigation's GitHub issues, and of course here on SO, but to no avail.
I'm using react navigation 5 (with react native) and my navigation set up looks like this:
// Root
<NavigationContainer>
<RootStack.Navigator mode="modal" headerMode="none">
<RootStack.Screen name="Auth" component={AuthStackNavigator} />
<RootStack.Screen name="App" component={BottomTabsNavigator} />
</RootStack.Navigator>
</NavigationContainer>
// App
<Tab.Navigator tabBar={bottomTabBar}>
<Tab.Screen name="ScreenA1" component={StackA} />
<Tab.Screen name="ScreenB1" component={StackB} />
</Tab.Navigator>
// TAB A
<StackA.Navigator headerMode="none">
<StackA.Screen name="ScreenA1" component={ScreenA1} />
<StackA.Screen name="ScreenA2" component={ScreenA2} />
</StackA.Navigator>
// TAB B
<StackB.Navigator headerMode="none">
<StackB.Screen name="ScreenB1" component={ScreenB2} />
<StackB.Screen name="ScreenB2" component={ScreenB2} />
</StackB.Navigator>
So inside my App i have 2 Bottom Tabs (Tab A and Tab B) each having 2 nested screens (Screen A1>A2 and B1>B2).
So when i tap on Tab A i go to ScreenA1 then in there i can move on to ScreenA2. Same for Tab B.
Now on ScreenB2 from Tab B i have a button that should navigate the user to Screen A2 with some data to prefill on that screen.
If i do it like this:
navigation.navigate('ScreenA1', {
screen: 'ScreenA2',
params: { data },
});
I'll land on ScreenA2 with the data, but:
If i visited ScreenA2 before, the previous state persists and i can see its old state and data.
If i never visited ScreenA2 before it's now not inside the Tab A Stack, but instead pushed on top of ScreenB2.
So i guess i would need to reset the screen before i navigate to it (or unmount whenever i leave it) and also make sure that it gets put inside the Tab A Stack on creation, so when i try to call goBack from Screen A2 i land on Screen A1. Maybe navigate back to root first and call the screen from there without the user seeing all screens flashing up.
You can replace
navigation.navigate('ScreenA1'...
By
navigate.push('ScreenA1'...
That way, a new route will be added to your navigation stack, with new state and props.
Resolved it with this:
navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [
{
name: 'ScreenA1',
state: {
routes: [
{
name: 'ScreenA1',
},
{
name: 'ScreenA2',
params: { data },
},
],
},
},
],
}),
);
Notice how i have 'ScreenA1' twice in there. I guess that's necessary because the Navigator and the first Screen inside the Navigator share the same route name
There is huge problem with bottom tab navigation on android that I'm struggling with and can't find any solution!!
on Android when keyboard shows the bottom tab bar goes upon the keyboard, which is obviously not a desired behaviour!
This happens when softInputMode in Android Manifest is set to adjustResize (which is the default mode for react native), I've tried with adjustPan and resolves the problem, but now when keyboard appears android avoids not only the view but either the header of the app! This behaviour too is not acceptable!
I've also tried with workarounds like listening to keyboard events (didShow, and didHide are only available) and disabling the bottom bar on keyboard appearingt but this leads to many glitches in the app.
The keyboardHidesTabBar prop is also very ugly cause it is an animation that hides the bar that starts after keyboard opening...
Have you tried this solution keyboardHidesTabBar: true in TabBarOptions props.
<Tab.Navigator
tabBarOptions={{
keyboardHidesTabBar: true,
}}
>
<Tab.Screen />
<Tab.Screen />
</Tab.Navigator>
Answer for React Navigation V5 with or without a Custom tabBar
if you use a custom tabBar the keyboardHidesTabBar: true prop is not working but when i change the custom tabBar to default tab bar that prop works but i don't like the behavior of that prop on android, so i used keyboard from react-native to check if the keyboard is active or not and setting the css display prop to 'none'
import { Keyboard, View } from "react-native";
const [keyboardStatus, setKeyboardStatus] = useState<boolean>();
useEffect(() => {
const showSubscription = Keyboard.addListener('keyboardDidShow', () => {
setKeyboardStatus(true);
});
const hideSubscription = Keyboard.addListener('keyboardDidHide', () => {
setKeyboardStatus(false);
});
return () => {
showSubscription.remove();
hideSubscription.remove();
};
}, []);
so on the custom tab bar component now we can do like this.
<View style={
[
styles.mainContainer,
keyboardStatus ? styles.hideTabNavigation : null,
]
}
>
// your code here
</View>
Hope this will help someone until they fix this issue!
I'm trying to create TabBar using react-navigation createBottomTabNavigator. I want to add this red thing above Tab when it's active. Any ideas how to achieve this? I was thinking about changing Tab container if it's active, but couldn't find how to do this.
If your tabs point to another stack navigators, the best idea would be to pass a custom component to tabBarIcon. Something like that:
SomeStack.navigationOptions = {
tabBarIcon: ({ focused }) => (
<TabBarButton
isFocused={focused} // render red thing above if focused
iconName="icon name"
/>
),
};