React-Native Header Title and Tab Navigator - react-native

I’ve a problem I cannot find an answer to, even though I’ve found similar problems with answers.
Basically, I pull a name (and other info) from a database. I then navigate to a screen (Screen A) in a tab navigator. I place the name into the header title of that screen (needed a stack navigator to do that I discovered from this site). I then have a second tab (Screen B) I can navigate to and want that same name placed in the header title there.
While on Screen B I also need to change the name and have that placed back into the header title of both Screen A and Screen B.
How do I do this? I have example code below that hopefully explains in more detail.
App.js
import React from 'react';
import { Text, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { HomeScreen, ScreenA, ScreenB } from './ui/screens.js';
const AStack = createStackNavigator();
function ScreenAStack(headerProps) {
return (
<AStack.Navigator>
<AStack.Screen name="Screen A" component={ScreenA} />
</AStack.Navigator>
);
}
const BStack = createStackNavigator();
function ScreenBStack(headerProps) {
return (
<BStack.Navigator>
<BStack.Screen name="Screen B" component={ScreenB} />
</BStack.Navigator>
);
}
const BottomTab = createBottomTabNavigator();
function Tabs() {
return (
<BottomTab.Navigator
screenOptions={{ headerShown: false }}
>
<BottomTab.Screen name="Screen A Stack" options={{ title: "Screen A" }}>
{(props) => (
<ScreenAStack {...props} />
)}
</BottomTab.Screen>
<BottomTab.Screen name="Screen B Stack" options={{ title: "Screen B" }}>
{(props) => (
<ScreenBStack {...props} />
)}
</BottomTab.Screen>
</BottomTab.Navigator>
);
}
const Stack = createStackNavigator();
const App = () => {
return (
<NavigationContainer>{
<Stack.Navigator>
<>
<Stack.Screen name="Home Screen" options={() => ({ headerShown: true })} component={HomeScreen} />
<Stack.Screen name="Screen A Tabs" options={() => ({ headerShown: false })} component={Tabs} />
</>
</Stack.Navigator>
}</NavigationContainer>
);
}
export default App;
screens.js
import React, { useEffect } from 'react';
import { View, Text, TextInput, StyleSheet, Button, } from 'react-native';
export function HomeScreen({ navigation }) {
// Get name from server using api and send to Screen A.
navList = (item) => {
navigation.navigate("Screen A Tabs", {
screen: 'Screen A Stack',
params: {
screen: 'Screen A',
params: { item },
},
});
}
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen.</Text>
<Button title="Go to Tab Screen" onPress={() => navList({name: "Name here!"})} />
</View>
);
}
export function ScreenA({ route, navigation }) {
// Place name into header title.
useEffect(() => {
navigation.setOptions({
title: route.params.item.name,
});
}, [route]);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Screen A</Text>
</View>
);
}
export function ScreenB({ navigation }) {
// Update header title with new name and update name on server using api.
const [newName, setNewName] = React.useState("");
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Screen B</Text>
<TextInput
value={newName}
onChangeText={newName => setNewName(newName)}
placeholder="Type new name here."
/>
<Button title="Change Name" onPress={() => console.log("Change name to " + newName)} />
</View>
);
}

I will give a short answer. your do not need to create a separate stacks for a single screen. here you can create a simple bottom tab navigator. just a sample.
<Tab.Navigator
initialRouteName="SearchScreen"
backBehavior="initialRoute"
screenOptions={{headerShown: false}}
>
<Tab.Screen
name="SearchScreen"
component={SearchScreen}
options={{
headerShown: false,
tabBarLabel: 'Search',
tabBarIcon: 'search',
}}
/>
<Tab.Screen
name="DialerScreen"
component={DialerScreen}
options={{
headerShown: false,
tabBarLabel: 'Dialer',
tabBarIcon: 'dialer',
}}
/>
</Tab.Navigator>
after this create a simple stack and add this bottom or top tab navigator like this
<Stack.Screen
name="Home"
component={TopTabNavigator}
options={{headerShown:true}}
/>
you can change title like this
onPress={() => {
if (item.is_analog) {
navigation.setOptions({title: 'Analog Speedo Meter'});
} else {
navigation.setOptions({title: 'Digital Speedo Meter'});
}
}}
cheers

Related

toggleDrawer from #react-navigation/drawer not working in React-Native

I made a nested navigation Drawer + Stack, but the drawer is not working, I am not able to toggle the drawer, but able to drag the drawer from the left side and it goes back closing automatically after releasing it.
Also, I get a pop up when hovering over navigation.toggleDrawer(), this is what I get
Property 'toggleDrawer' does not exist on type
'NavigationProp<ParamListBase, string, Readonly<{ key: string; index:
number; routeNames: string[]; history?: unknown[]; routes:
NavigationRoute<ParamListBase, string>[]; type: string; stale: false;
}>, {}, {}>'
So what's wrong here and how to fix it? And can I make a drawer without using #react-navigation/drawer ?
app.js :
import React from "react";
import { NavigationContainer } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
import HomeScreen from "./src/screens/HomeScreen";
import MovieScreen from "./src/screens/MovieScreen";
import FavoriteScreen from "./src/screens/FavoritesScreen";
import { useFonts } from "expo-font";
import AppLoading from "expo-app-loading";
import { createDrawerNavigator } from "#react-navigation/drawer";
import CustomDrawerContent from "./src/components/DrawerContent";
const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
function HomeStackScreen() {
return (
<Stack.Navigator>
<Stack.Screen
name='home'
component={HomeScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name='movie'
component={MovieScreen}
options={{ headerShown: false }}
/>
</Stack.Navigator>
);
}
export default () => {
const [fontLoaded] = useFonts({
Regular: require("./assets/fonts/NunitoSans-Regular.ttf"),
Bold: require("./assets/fonts/NunitoSans-Bold.ttf"),
Black: require("./assets/fonts/NunitoSans-Black.ttf"),
ExtraBold: require("./assets/fonts/NunitoSans-ExtraBold.ttf"),
ExtraLight: require("./assets/fonts/NunitoSans-ExtraLight.ttf"),
Light: require("./assets/fonts/NunitoSans-Light.ttf"),
SemiBold: require("./assets/fonts/NunitoSans-SemiBold.ttf"),
});
return fontLoaded ? (
<NavigationContainer>
<Drawer.Navigator
drawerContent={(props) => <CustomDrawerContent {...props} />}
initialRouteName='Home'
screenOptions={{ drawerPosition: "left", drawerType: "front" }}
>
<Drawer.Screen
name='home'
component={HomeStackScreen}
options={{ headerShown: false }}
/>
<Drawer.Screen
name='fav'
component={FavoriteScreen}
options={{ headerShown: false }}
/>
</Drawer.Navigator>
</NavigationContainer>
) : (
<AppLoading />
);
};
home.js
import { useNavigation } from "#react-navigation/native";
export default function HomeScreen(){
const navigation = useNavigation();
return(
<ScrollView style={styles.container}>
<StatusBar
style='auto'
translucent={false}
backgroundColor={COLORS.BASIC_BACKGROUND}
/>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingHorizontal: 20,
paddingVertical: 10,
}}
>
<Pressable
style={{ flex: 1 }}
onPress={() => {
navigation.toggleDrawer();
console.log("Clicked on Menu Bar");
}}
>
<Image
style={{ width: 25, height: 28 }}
source={{
uri: "https://img.icons8.com/material-rounded/96/000000/menu--v1.png",
}}
/>
</Pressable>
</View>
</ScrollView>
)
}

How to define routes in React Native without displaying them in a navigator?

I have a bottom tab navigator made with React Navigation on a basic React Native app. It only gives access to 4 screens. Some screens may contains buttons that would open a new page (for example: a user profile page). How to define the route without displaying it in the bottom tab navigator?
The code:
const Tab = createBottomTabNavigator();
export default function Router() {
const { t } = useTranslation();
return (
<Tab.Navigator initialRouteName={screen.home}>
<Tab.Screen name={screen.home} component={Home}/>
<Tab.Screen name={screen.a} component={A}/>
<Tab.Screen name={screen.b} component={B}/>
<Tab.Screen name={screen.c} component={C}/>
</Tab.Navigator>
);
}
The idea would be to allow a click like that:
function ScreenB(){
const navigation = useNavigation();
return (
<TouchableOpacity onPress={() => navigation.navigate("/user/123")}>
<Text>see user 123</Text>
</TouchableOpacity>
)
}
Thanks for your help!
Online example https://snack.expo.io/#vasylnahuliak/great-bubblegum
import * as React from 'react';
import { Button, Text, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const HomeScreen = ({ navigation }) => {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home screen</Text>
</View>
);
};
const ProfileScreen = ({ navigation }) => {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
};
const DetailsScreen = () => { // <--- Screen without navigation header
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Details screen</Text>
</View>
);
};
const ProfileStack = createStackNavigator();
const ProfileStackScreen = () => {
return (
<ProfileStack.Navigator>
<ProfileStack.Screen name="Settings" component={ProfileScreen} />
</ProfileStack.Navigator>
);
};
const MainTab = createBottomTabNavigator();
const MainTabs = () => {
return (
<MainTab.Navigator>
<MainTab.Screen name="Home" component={HomeScreen} />
<MainTab.Screen name="Profile" component={ProfileStackScreen} />
</MainTab.Navigator>
);
};
const RootStack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
<RootStack.Navigator>
<RootStack.Screen
name="MainTabs"
component={MainTabs}
options={{ headerShown: false }}
/>
<RootStack.Screen name="Details" component={DetailsScreen} /> // <--- Screen without navigation header
</RootStack.Navigator>
</NavigationContainer>
);
}
Official documentation: https://reactnavigation.org/docs/hiding-tabbar-in-screens

React Navigation 5.x nested navigation drawer not opening from menu button

When I swipe right my drawer opens, but I want it to open using a button in the header. I have place the DrawerNavigator 'createDrawer ' in side the StackNavigator'createHomeStack'.
I am getting this error:
Reference Error: Can't find variable : Navigation
I also tried this: options={({ navigation }) => ({ but then I get error:
TypeError: navigation.toggleDrawer is not a function. (In 'navigation.toggleDrawer()', 'navigation.toggleDrawer' is undefined)
Code:
import React from 'react';
import { TouchableOpacity } from 'react-native';
import {
NavigationContainer,
DrawerActions,
DefaultTheme,
DarkTheme,
useNavigation,
} from '#react-navigation/native';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { createStackNavigator } from '#react-navigation/stack';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import {
Appearance,
useColorScheme,
AppearanceProvider,
} from 'react-native-appearance';
import Feed from './src/feed';
import Detail from './src/detail';
import Screen1 from './src/screens/drawer/screen1';
import Screen2 from './src/screens/drawer/screen2';
import Screen3 from './src/screens/drawer/screen3';
import Tab1 from './src/screens/tabs/tab1';
import Tab2 from './src/screens/tabs/tab2';
const Drawer = createDrawerNavigator();
const Stack = createStackNavigator();
App = () => {
const colorScheme = useColorScheme();
const MyTheme = {
dark: false,
colors: {
primary: 'white',
background: 'white',
card: '#65509f',
text: 'white',
border: 'green',
},
};
createHomeStack = () => (
<Stack.Navigator>
<Stack.Screen
name='Home'
children={this.createDrawer}
options={{
title: 'Home Screen',
headerLeft: () => (
<TouchableOpacity onPress={() => navigation.toggleDrawer()}>
<Icon
name='menu'
style={[{ color: 'white', marginLeft: 16 }]}
size={25}
></Icon>
</TouchableOpacity>
),
}}
/>
<Stack.Screen
name='Detail'
component={Detail}
options={{
title: 'Detail Screen',
}}
/>
<Stack.Screen name='Bottom Tabs' component={Tab1} />
<Stack.Screen name='Top Tabs' component={Tab2} />
</Stack.Navigator>
);
createDrawer = () => (
<Drawer.Navigator>
<Drawer.Screen name='Feed' component={Feed} />
<Drawer.Screen name='Contacts' component={Screen1} />
<Drawer.Screen name='Favorites' component={Screen2} />
<Drawer.Screen name='Settings' component={Screen3} />
</Drawer.Navigator>
);
return (
<AppearanceProvider>
<NavigationContainer theme={colorScheme == 'dark' ? DarkTheme : MyTheme}>
{this.createHomeStack()}
</NavigationContainer>
</AppearanceProvider>
);
};
export default App;
There are 2 approaches to handle this:
Nest the stack inside the drawer instead of drawer inside stack
Use dispatch instead of toggleDrawer:
import { DrawerActions } from '#react-navigation/native';
// ...
<TouchableOpacity onPress={() => navigation.dispatch(DrawerActions.toggleDrawer())}>
Read more about how nesting works here https://reactnavigation.org/docs/nesting-navigators#navigator-specific-methods-are-available-in-the-navigators-nested-inside
import { DrawerActions } from '#react-navigation/native';
// ...
options={({ navigation }) => ({
title: 'Home Screen',
headerLeft: () => (
<TouchableOpacity style={{ paddingLeft: 20 }}>
<Icon
name='menu'
size={25}
style={[{ color: 'black' }]}
onPress={() =>
navigation.dispatch(DrawerActions.toggleDrawer())
}
/>
</TouchableOpacity>
),
})}
You need to get navigation prop from option the dispatch drawer Action when Button is pressed. Try the below code:
options={({navigation}) => ({
title: 'Home Screen',
headerLeft: () => (
<TouchableOpacity onPress={() => navigation.dispatch(DrawerActions.toggleDrawer())}>
<Icon
name='menu'
style={[{ color: 'white', marginLeft: 16 }]}
size={25}
></Icon>
</TouchableOpacity>
),
})}

how to handle the Navigation in Seprate file in React Native

I am new to React native
I want to manage navigation in a component separate from App.js Component.
I could not understand how to handle in a separate component file
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer style={{ backgroundColor: '#FFFFFF' }}>
<Stack.Navigator>
<Stack.Screen name=" " component={FirstPage}
options={{
headerLeft: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#fff"
/>
),
}}
/>
<Stack.Screen name="Login" component={LoginView}
options={{
headerStyle: {
backgroundColor: '#88aa31',
},
headerTintColor: '#fff',
headerLeft: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#fff"
/>
),
}}
/>
<Stack.Screen name="Signup" component={SignUpView}
options={{
headerStyle: {
backgroundColor: '#88aa31',
},
headerTintColor: '#fff',
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
This my App.js file. How can i manage navigation in a separated component in React Native?
Create navigation file import all other components you want as show below in code.
import { createAppContainer, createStackNavigator } from "react-navigation";
import LoginView from './App/component/Views/Login'
import SignUpView from './App/component/Views/Signup';
const SwitchNavigator = createStackNavigator(
{
LoginView: { screen: LoginView },
SignUpView: { screen: SignUpView }
},
{
initialRouteName: 'LoginView',
}
);
const App = createAppContainer(SwitchNavigator)
export default App;

How can I change the title for each screen inside TabNavigator? - React Navigation

My stack navigator
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="PageA" component={PageA} options={{title:'Page=A'}} />
<Stack.Screen name="PageB" component={PageB} options={{title:'Page=B'}} />
<Stack.Screen name="Menu" component={MenuTabNavigator} options={{title:'Menu'}} />
</Stack.Navigator>
</NavigationContainer>
and my tab navigator
const MenuTabNavigator = () => {
return (
<Tab.Navigator>
<Tab.Screen name="PageA" component={PageA} />
<Tab.Screen name="PageB" component={PageB} />
<Tab.Screen name="Menu" component={Menu} />
</Tab.Navigator>
);};
I'm using Tab Navigator with Stack Navigator.
ScreenA, Screen B and Menu screen in my Tabs.
I pass MenuTabNavigator to StackNavigator's Menu Component as you can see.
Problem:
When I use tabs, header title stays 'Menu'.
For example when I touch to PageB on tab, i expect header title should be 'PageB' but it stays 'Menu'.
How can I change header title for screens when i use bottom tabs?
The approach you are using is wrong. if you go this way you have to create three StackNavigators so that you can get three different headers. and then wrap them in a tab navigator. but this is the wrong way to use it.
import * as React from 'react';
import { View, Text } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
function HomeScreen({ navigation }) {
navigation.setOptions({ title: 'Home' })
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home!</Text>
</View>
);
}
function SettingsScreen({ navigation }) {
navigation.setOptions({ title: 'Setting' })
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings!</Text>
</View>
);
}
function Menu({ navigation }) {
navigation.setOptions({ title: 'Menu' })
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Menu</Text>
</View>
);
}
const StackHome = () => {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
);
};
const StackSetting = () => {
return (
<Stack.Navigator>
<Stack.Screen name="Setting" component={SettingsScreen} />
</Stack.Navigator>
);
};
const StackMenu = () => {
return (
<Stack.Navigator>
<Stack.Screen name="Menu" component={Menu} />
</Stack.Navigator>
);
};
const Tab = createBottomTabNavigator();
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="PageA" component={StackHome} options={{ title: "Home" }} />
<Tab.Screen name="PageB" component={StackSetting} options={{ title: "Settings"
}}
/>
<Tab.Screen name="Menu" component={StackMenu} options={{ title: "Menu" }} />
</Tab.Navigator>
</NavigationContainer>
);
}
export default App;