Can we have different custom drawer contents - react-native

I have to Drawer Screens. Both require custom drawer content. I like to know if we can have one custom drawer content for HomeDrawer and a completely different drawer content for the PanelDrawer
<Drawer.Navigator
initialRouteName="HomeDrawer"
edgeWidth={0}
// swipeEdgeWidth={0}
drawerContent={props => <DrawerContent {...props} />}>
<Drawer.Screen
name="HomeDrawer"
options={{
title: headerTitleEnabled ? 'Hello App' : '',
headerStyle: {
backgroundColor: Colors.primary,
},
headerTintColor: '#fff',
headerRight: props => <HeaderButtons {...props} />,
}}
component={MainTabNavigator}
/>
<Drawer.Screen
name="PanelDrawer"
options={{
title: headerTitleEnabled ? 'Hello App' : '',
headerStyle: {
backgroundColor: Colors.primary,
},
headerTintColor: '#fff',
headerRight: props => (
<HeaderButtonsCustom{...props} />
),
}}
component={CustomLayoutNavigator}
/>

Of course, you can nest the drawers if that's the case you want:
Nesting navigators means rendering a navigator inside a screen of another navigator
const MainDrawer = createDrawerNavigator();
const HomeDrawer = createDrawerNavigator();
const PanelDrawer = createDrawerNavigator();
const HomeScreen = () => (
<HomeDrawer.Navigator>
{/*Home drawer custom stuff*/}
</HomeDrawer.Navigator>
);
const PanelScreen = () => (
<PanelDrawer.Navigator>
{/*Panel drawer custom stuff*/}
</PanelDrawer.Navigator>
);
const Navigator = () => (
<MainDrawer.Navigator>
<MainDrawer.Screen name="HomeScreen" component={HomeScreen} />
<MainDrawer.Screen name="PanelScreen" component={PanelScreen} />
</MainDrawer.Navigator>
);
You can read more about nesting navigators in the official documentation.

Related

React Native Drawer Navigation show headerLeft

I have a Drawer navigator which is inside a Stack navigator and I'd like to display a header. Currently I can display everything I want however because the header is defined at the Stack level the navigation inside the header is stack level not drawer level which is preventing me from opening the drawer.
Root stack
<Stack.Navigator
initialRouteName={"Splash"}
screenOptions={{}}
component={SplashScreen}
>
{ auth ?
<Stack.Screen name="Drawer" component={DrawerStack} options={({ navigation }) => ({
title: 'My App',
headerLeft: () => (
<HeaderLeft navigation={ navigation } />
),
headerRight: () => (
<HeaderRight navigation={ navigation } />
),
headerTitleAlign: 'center',
headerTintColor: 'white',
headerStyle: {
backgroundColor: '#5742f5'
},
})} />
:
<Stack.Screen name="Auth" component={AuthStack} options={{
headerShown: false
}}/>
}
</Stack.Navigator>
Drawer stack
<Drawer.Navigator options={{
headerShown: true,
headerLeft: () => (
<HeaderLeft navigation={ navigation } />
),
}}>
<Drawer.Screen
name="Conversations"
options={{
title: props.title,
}}
component={ChatListScreen}
/>
<Drawer.Screen
name="ChatRoom"
options={{
drawerLabel: () => null,
title: null,
drawerIcon: () => null
}}
component={ChatRoomScreen}
/>
</Drawer.Navigator>
Note in the drawer navigator the line with headerLeft does nothing and is there to show where I attempted to put it thinking it would work. I did think it might be overlaying the stack one so I commented out the stack one and it didn't work.
HeaderLeft
export default function HeaderLeft ({ navigation }) {
const openMenu = () => {
navigation.toggleDrawer();
}
return (
<View style={styles.header}>
<Icon name='menu' onPress={openMenu} size={28} style={styles.icon} color="white"/>
</View>
)
}
My question is how can I refactor this to enable me to have the HeaderLeft component work to open the drawer. I will be adding more screens so ideally something I don't have to pass to each screen but if that is what works I am good with it too.
Options in DrawerStack not work. I modified it:
<Drawer.Navigator
screenOptions={{
headerLeft: () => <HeaderLeft />,
}}>
// ...
</Drawer.Navigator>
And a little change in HeaderLeft:
import { useNavigation } from '#react-navigation/native';
function HeaderLeft() {
const navigation = useNavigation();
const openMenu = () => {
navigation.toggleDrawer();
};
// render your Button
}
Demo: https://snack.expo.dev/#pqv2210/0d613b

Can't call a nested navigation screen from a deep component in react native

I have followed this tutorial and created Drawer and Main Tab Navigations for my react native expo app.
Now I need a screen which should not be listed in Drawer or Tab and which is needed to be called from a deep component where navigations props are not being sent.
I tried useNavigation Hook but got error where react native is unable to find any such screen name.
PFB the tentative sample codes:
Main Tab called from App.js
const Tab = createMaterialBottomTabNavigator();
const HomeStack = createStackNavigator();
const ProfileStack = createStackNavigator();
const ListStack = createStackNavigator();
const MainTabScreen = () => (
<Tab.Navigator
initialRouteName="Home"
activeColor="#fff"
barStyle={{ backgroundColor: '#009387' }}
>
<Tab.Screen
name="Home"
component={HomeStackScreen}
options={{
tabBarLabel: 'Home',
tabBarColor: '#009387',
tabBarIcon: ({ color }) => (
<Icon name="home" color={color} size={26} />
),
}}
/>
<Tab.Screen
name="Profile"
component={ProfileStackScreen}
options={{
tabBarLabel: 'Profile',
tabBarColor: '#1f65ff',
tabBarIcon: ({ color }) => (
<Icon name="aperture" color={color} size={26} />
),
}}
/>
</Tab.Navigator>
);
export default MainTabScreen;
const HomeStackScreen = ({navigation}) => (
<HomeStack.Navigator screenOptions={{
headerStyle: {
backgroundColor: '#009387',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold'
}
}}>
<HomeStack.Screen name = "Home" component = {Home}
options = {{
title: 'Overview',
headerLeft: () => (
<Icon.Button name="menu" size={25}
backgroundColor="#009387" onPress={()=>navigation.openDrawer()}
></Icon.Button>
)
}}
/>
</HomeStack.Navigator>
);
const ProfileStackScreen = ({navigation}) => (
<ProfileStack.Navigator screenOptions={{
headerStyle: {
backgroundColor: '#d02860',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold'
}
}}>
<ProfileStack.Screen name = "Profile" component = {Profile}
options = {{
headerLeft: () => (
<Icon.Button name="menu" size={25}
backgroundColor="#d02860" onPress={()=>navigation.openDrawer()}
></Icon.Button>
)
}}
/>
</ProfileStack.Navigator>
);
const ListStackScreen = ({navigation}) => (
<NotificationsStack.Navigator screenOptions={{
headerStyle: {
backgroundColor: '#694fad',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold'
}
}}>
<ListStack.Screen name = "List" component = {List}
options = {{
headerLeft: () => (
<Icon.Button name="menu" size={25}
backgroundColor="#694fad" onPress={()=>navigation.openDrawer()}
></Icon.Button>
)
}}
/>
</NotificationsStack.Navigator>
);
useNavigation component section:
import { useNavigation } from '#react-navigation/native';
.
.
.
const SomeStuff = ({item}) => {
......
<ListButton title={Count} screenName="ListStackScreen" />
.....
}
.
.
function ListButton({ title, screenName }) {
const navigation = useNavigation();
return (
<Button
title={`${title} Total`}
onPress={() => navigation.navigate(screenName)}
/>
);
}
also tried:
navigation.navigate('HomeDrawer',{screen: screenName})
I need to call the above ListStackScreen from a deep component. I tried using navigation.navigate(ListStackScreen) but it doesn't work as explained above.
Please let me know how to use the screen without displaying it in any Drawer or Tab visually.
Edit: Update after trying the given answer
I do have this in the main app.js also:
<Drawer.Screen name="HomeDrawer" component={MainTabScreen} />
This setup should allow you to navigate between HomeStackScreen and ListStackScreen.
const Stack = createStackNavigator();
function HomeStack() {
return (
<Stack.Navigator
initialRouteName="HomeStackScreen">
<Stack.Screen
name="HomeStackScreen"
component={HomeStackScreen}
/>
<Stack.Screen
name="ListStackScreen"
component={ListStackScreen}
/>
</Stack.Navigator>
);
}
<NavigationContainer>
<Tab.Navigator
initialRouteName="HomeStack">
<Tab.Screen
name="HomeStack"
component={HomeStack}
}}
/>
…
You can nest stacknavigator into Drawer, the inner stacknavigator screen won‘t be shown in Drawer.
I have created an sample

react-navigation [v6] drawer and stack nesting issues

Like the most common use case, I want to have a drawer in my react-native application where, from all screens header I can open/close the drawer, navigate to them. Also, each screen has an option (button, card,...) that on press should navigate to any other screen.
So I have defined the following structure. But there are several problems;
Drawer cannot recognize which screen in on focus now.
Drawer type check does not give auto-suggestions on navigation prop (e.g. props.navigation.navigate(" /* no suggestion on the present screens in the stack*/"))
On Android I feel that the overall performance drops significantly
So is this a good structure? From the official documentation, I could not find any hint on how to implement it? stack nested in drawer or vise-versa?
export type MainStackParamList = {
HomeScreen: undefined;
OverViewScreen: undefined;
WorkOrdersScreen: {id?: number; description?: string; tabIndex?: number};
PropertiesScreen: undefined;
PropertyDetailScreen: {propertyUnit: PropertyUnit};
};
export type MainDrawerParamList = {MainStack: NavigatorScreenParams<MainStackParamList>};
export type AppNavigationCompositeProps = CompositeScreenProps<
DrawerScreenProps<MainDrawerParamList, 'MainStack'>,
StackScreenProps<MainStackParamList>
>;
//____The navigation part______
const MainStack = createStackNavigator<MainStackParamList>();
const Drawer = createDrawerNavigator<MainDrawerParamList>();
/* the composite type is the only way I found to have access to
drawer fucntions such as toggleDrawer in the stack screens*/
const MainStackScreens = (navigation: AppNavigationCompositeProps) => (
<MainStack.Navigator initialRouteName={'HomeScreen'} screenOptions={MainStackScreenOptions(navigation)}>
<MainStack.Screen name="HomeScreen" component={HomeScreen} />
<MainStack.Screen name="OverViewScreen" component={OverViewScreen} />
<MainStack.Screen name="WorkOrdersScreen" component={WorkOrdersScreen} />
<MainStack.Screen name="PropertiesScreen" component={PropertiesScreen} />
<MainStack.Screen name="PropertyDetailScreen" component={PropertyDetailScreen} />
</MainStack.Navigator>
);
const Navigation: React.FC<{}> = () => {
return (
<NavigationContainer>
<Drawer.Navigator
initialRouteName="MainStack"
screenOptions={{headerShown: false}}
drawerContent={props => <CustomDrawerContent {...props} />}>
<Drawer.Screen name="MainStack" component={MainStackScreens} />
</Drawer.Navigator>
</NavigationContainer>
);
};
export default Navigation;
const MainStackScreenOptions = (navigation: AppNavigationCompositeProps): StackNavigationOptions => {
return {
headerStyle: {backgroundColor: '#00aade'},
headerTintColor: '#fca903',
headerTitleAlign: 'center',
headerTitleStyle: {fontWeight: 'bold', fontStyle: 'italic'},
headerBackTitle: 'GoBack',
headerLeft: () => <IconButton icon="menu" color="white" onPress={() => navigation.navigation.openDrawer()} />
};
};
//___the drawer content is like
const CustomDrawerContent: React.FC<DrawerContentComponentProps> = props => {
return (
<DrawerContentScrollView>
<Drawer.Item label="Home" onPress={() => props.navigation.navigate('HomeScreen')} icon="star" />
<Drawer.Item label="OverView" onPress={() => props.navigation.navigate('OverViewScreen')} icon="star" />
<Drawer.Item label="WorkOrders" onPress={() => props.navigation.navigate('WorkOrdersScreen')} icon="star" />
<Drawer.Item label="Properties" onPress={() => props.navigation.navigate('PropertiesScreen')} icon="star" />
</DrawerContentScrollView>
);
};

how to get drawerOpen() to stack navigator screen Options

i have this stack navigator:
const StackNavigator = () => (
<Stack.Navigator headerMode="screen" navigationOptions screenOptions={ss}>
<Stack.Screen name="Main" component={MainScreen} headerMode="screen" />...............
and i have object of screen options:
const ss = {
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
gestureEnabled: true,
gestureDirection: "horizontal",
headerTitleAlign: "center",
headerHideShadow: true,
headerTintColor: "red",
headerStyle: {
borderBottomWidth: 0.5,
borderColor: "white",
backgroundColor: "#1d2731",
},
headerTitleStyle: {
color: "white",
},
headerLeft: () => <AnimateHamburger navigation={navigation} />,
};
no when i get inside the component AnimatedHamburger
i dont get the props of openDrawer ...i get "...
navigation.openDrawer is not function....................
the AnimateHamburger component:
const AnimateHamburger = ({ navigation }) => {
console.log(navigation);
const isDrawerOpen = useIsDrawerOpen();
const toggleMenu = () => {
navigation.openDrawer();
};
return (
<View style={styles.icon}>
<Icon
style={styles.icon}
onPress={toggleMenu}
name={isDrawerOpen ? "enter" : "bars"}
type={isDrawerOpen ? "antdesign" : "font-awesome"}
color="black"
size={30}
/>
</View>
);
};
PLEASE help me how can i get the navigation props to toggle my drawer from the AnimateHamburger component
If you receiving openDrawer is not a function that's probably you are trying to access this method in a nested navigator which don't know about the parent drawer navigator. You can access drawer methods with DrawerAction from react-navigation take a look here:
https://reactnavigation.org/docs/drawer-actions
And use it like this:
https://reactnavigation.org/docs/nesting-navigators/#navigator-specific-methods-are-available-in-the-navigators-nested-inside

react-navigation: Navigate to a different screen from a button in header

I have a Icon on the right side of my header and on press of that button i want to navigate to a different screen.
I have searched very much for this but all of the solutions are for class components and there are no official documentation available for it.
I am using react native version 0.61.4.
On press of the icon in the header on the right i want to move the 'ProfileScreen'. All the other navigation is working fine. I have a button in 'HomeScreen' to move to 'ResultsScreen' but cannot go to 'ProfileScreen' from the header.
Here is snippet of my code
const Stack = createStackNavigator();
const App = () => {
return (
<SafeAreaView style={{ flex: 1 }}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={
{
title: 'Home',
headerStyle: {
backgroundColor: '#273469',
},
headerTintColor: '#EBF2FA',
headerRight: () => (
<Icon
onPress={() => navigate('ProfileScreen')}
name="edit"
type="material"
/>
),
}
}
/>
<Stack.Screen
name="ResultsScreen"
component={ResultsScreen}
/>
<Stack.Screen
name="ProfileScreen"
component={ProfileScreen}
/>
</Stack.Navigator>
</NavigationContainer>
</SafeAreaView>
)
}
options can take a function as an argument and this function takes props as a parameter.
Here is the documentation
Here is the TypeScript definition for information:
* Navigator options for this screen.
*/
options?: ScreenOptions | ((props: {
route: RouteProp<ParamList, RouteName>;
navigation: any;
}) => ScreenOptions);
as you can see props, contain the navigation object that you can use to call navigate like this :
options={({ navigation }) => ({
title: 'Home',
headerStyle: {
backgroundColor: '#273469',
},
headerTintColor: '#EBF2FA',
headerRight: () => (
<Icon
onPress={() => navigation.navigate('ProfileScreen')}
name="edit"
type="material"
/>
),
})}
Adding to Kevin's answer, you can also add a simple button in the header:
options={({ navigation }) => ({
title: 'Home',
headerStyle: {
backgroundColor: '#273469',
},
headerTintColor: '#EBF2FA',
headerRight: () => (
<Button // a button in the header!
onPress={() =>
navigation.navigate('Account')}
title="Account"
/>
),
})}
Here is what worked for me:
<Stack.screen
name="YourScreenName"
headerShown=true
options={({ navigation }) => ({
title: 'YourOtherScreenName',
headerRight: () => (
<TouchableOpacity
onPress={() => navigation.dispatch(StackActions.push('articleEdit', {articleId: 'new'}))} >
<View style={SomeStyleWithPaddingYouLike}>
<JSX_That_Renders_Your_Icon />
</View>
</TouchableOpacity>
),
})}
/>