perform card navigation with react-native-tab-view stationary? - react-native

I am currently using react-navigation and react-native-tab-view. If I want to navigate to another screen, but keep the tab view at the same place, how would I do that?

Using the React Navigation and React Native Tab View tutorials, you can rewrite the default React Native App.js file to look like the React Native Tab View's TabViewExample component. Wrap the TabViewExample component in React Navigation's NavigationContainer component.
Then. wrap the TabViewExample's FirstRoute component in Stack.Navigator component, move the FirstRoute component's code to a new component, e.g. FirstRouteScreen, and add another component to the FirstRoute's component, e.g. DetailsScreen. These will be the two screens that you want to navigate between whilst keeping the tab view at the same place.
Add a button to the FirstRouteScreen component which will allow you to navigate to the DetailsScreen.
The following is what it should look like:
import { NavigationContainer } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
import React from "react";
import { Button, Dimensions, StyleSheet, Text, View } from "react-native";
import "react-native-gesture-handler";
import { SceneMap, TabView } from "react-native-tab-view";
function FirstRouteScreen({navigation}) {
return (
<View style={[styles.scene, { backgroundColor: "#ff4081" }]}>
<Text>First Route</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate("Details")}
/>
</View>
);
}
function DetailsScreen() {
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>Details Screen</Text>
</View>
);
}
const FirstRoute = () => (
<Stack.Navigator initialRouteName="First Route">
<Stack.Screen
name="First Route"
component={FirstRouteScreen}
options={{ title: "Overview" }}
/>
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
);
const SecondRoute = () => (
<View style={[styles.scene, { backgroundColor: "#673ab7" }]}>
<Text>Second Route</Text>
</View>
);
const initialLayout = { width: Dimensions.get("window").width };
const Stack = createStackNavigator();
export default function App() {
const [index, setIndex] = React.useState(0);
const [routes] = React.useState([
{ key: "first", title: "First" },
{ key: "second", title: "Second" },
]);
const renderScene = SceneMap({
first: FirstRoute,
second: SecondRoute,
});
return (
<NavigationContainer>
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={initialLayout}
/>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
scene: {
flex: 1,
},
});
References:
Github. satya164/react-native-tab-view: A cross-platform Tab View component for React Native. https://github.com/satya164/react-native-tab-view. (Accessed November 22, 2020)
React Navigation. Getting started | React Navigation. https://reactnavigation.org/docs/getting-started. (Accessed November 22, 2020)
React Navigation. Hello React Navigation | React Navigation. https://reactnavigation.org/docs/hello-react-navigation. (Accessed November 22, 2020)
Moving between screens | React Navigation. https://reactnavigation.org/docs/navigating. (Accessed November 22, 2020)

Related

Delayed rendering of parent Drawer header inside Bottom Tab

I have BottomTab navigator with 2 screens Home and Activity, nested inside a Drawer Navigator. When I switch from one screen to second one using BottomTab, my header of Drawer Navigator hides with flickering effect and same thing happens again when I show it up on previous screen. I am handling headerShown:true and headerShown:false in listeners prop of Tab.Screen using focus and blur of that screen.
It seems like header is rendering after rendering of components below it. This header showing and hiding has more delay if I have multiple components inside both screens.
Snack repo is attached.
https://snack.expo.dev/#a2nineu/bottomtab-inside-drawer
I suggest you use Interaction Manager
As react-native is single threaded, JS gets blocked while running animation which can cause UI to lag and jitter.
Attaching code with changes
import 'react-native-gesture-handler';
import * as React from 'react';
import {
Text,
View,
StyleSheet,
Image,
InteractionManager,
} from 'react-native';
import Constants from 'expo-constants';
import { NavigationContainer } from '#react-navigation/native';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { createMaterialBottomTabNavigator } from '#react-navigation/material-bottom-tabs';
const Drawer = createDrawerNavigator();
const Tab = createMaterialBottomTabNavigator();
const Home = (props) => {
React.useEffect(() => {
const interactionPromise = InteractionManager.runAfterInteractions(() =>
setShowContent(true)
);
return () => interactionPromise.cancel();
}, []);
const [showContent, setShowContent] = React.useState(false);
return showContent ? (
<View>
<Text>Home</Text>
<Image
source={{
uri: 'https://images.unsplash.com/photo-1596265371388-43edbaadab94?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80',
}}
style={{ height: 500, width: 500 }}
resizeMode={'cover'}></Image>
</View>
) : null;
};
const Activity = () => {
return <Text>Activity</Text>;
};
const BottomTab = () => {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={Home} />
<Tab.Screen
name="Activity"
component={Activity}
listeners={({ navigation, route }) => ({
focus: () => {
navigation.getParent().setOptions({
headerShown: false,
});
},
blur: () => {
navigation.getParent().setOptions({
headerShown: true,
});
},
})}
/>
</Tab.Navigator>
);
};
export default function App() {
return (
<View style={styles.container}>
<NavigationContainer>
<Drawer.Navigator screenOptions={{ headerMode: 'float' }}>
<Drawer.Screen
options={{ headerStyle: { height: 100 } }}
name="BottomTab"
component={BottomTab}
/>
</Drawer.Navigator>
</NavigationContainer>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});

React Native — React-Navigation back button on wrong side of appbar?

Okay so I'm just jumping into React Native and I'm going through the docs with the react-navigation package. Whenever a screen is pushed onto the stack, the animation goes from right-left by default—Also I'm noticing the back button is on the right side of the appbar instead of the left be default. Is this by design or have I set something up incorrectly?
Also ignore the FC I'm using, I know it's not recommended but I'm just getting a feel for RN 😅
See image and code below:
import { StatusBar } from "expo-status-bar";
import { Button, StyleSheet, Text, View } from "react-native";
import { createNativeStackNavigator } from "#react-navigation/native-stack";
import { BaseNavigationContainer } from "#react-navigation/native";
import { FC } from "react";
import { StackScreenProps } from "./Types";
const Home: FC<StackScreenProps> = ({ navigation }) => {
return (
<View style={styles.container}>
<Text>Hello World </Text>
<Button
title="Switch Page"
onPress={() => {
navigation.navigate("About");
}}
/>
</View>
);
};
const About: FC<StackScreenProps> = ({ navigation }) => {
return (
<View style={styles.container}>
<Text>Learn the Process First</Text>
<Button title="Go Back" onPress={() => navigation.goBack()} />
</View>
);
};
const Stack = createNativeStackNavigator();
export default function App() {
return (
<BaseNavigationContainer>
{/* #ts-ignore */}
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="About" component={About} />
</Stack.Navigator>
</BaseNavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});

React Navigation6 Header issue

i'm building application with ReactNative and Expo, i building component called Header showing the title of each screen,
so i send props of name on each time render the component
<Header title={"Home Screen"}/>
Header.js comoment
import React,{ useState } from 'react';
import { Text, View, Image,StyleSheet} from 'react-native';
import Icon from 'react-native-vector-icons/Feather';
import { useNavigation } from "#react-navigation/native";
const Header=( navigation, title) =>{
const { navigate } = useNavigation();
return(
<View style={styles.container}>
<Icon name="user" size={28}/>
<Text style={{fontWeight:'bold'}} >{title}</Text>
<Icon name="bell" size={28} />
</View>
)
}
export default Header;
const styles = StyleSheet.create({
container:{
position:"absolute",
display:'flex',
justifyContent: 'space-between',
flexDirection:'row',
height: 60,
top:10 ,
left:6,
right:6,
elevation:0,
backgroundColor: '#fff',
alignItems: 'center',
}
})
this error showing:
Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead.
You just missed to de-structure your props in Header. Simply change your Header component to have
const Header=({ title }) =>{ // added {} to de-structure your props
const { navigate } = useNavigation();
return(
<View style={styles.container}>
<Icon name="user" size={28}/>
<Text style={{fontWeight:'bold'}} >{title}</Text>
<Icon name="bell" size={28} />
</View>
)
}

Having two tab navigation bars in React Navigation

I want to create an app, that has both a fixed bottom and top tab navigation bar.
See image:
After I finished the bottom navigation bar I tried the following in my App.js file:
return(
<NavigationContainer>
<Tab.Navigator> //top navbar
<Tab.Screen />
...
</Tab.Navigator>
<Tab.Navigator> //bottom navbar
<Tab.Screen />
...
</Tab.Navigator>
</NavigationContainer>
)
However, I get the error, that another navigator is already registered in this container and that I should not have multiple navigators under a single NavigationContainer.
I found multiple guides about nesting tab and stack navigators, but how do I nest multiple tab navigators, that both update the central screen?
AFAIK that is not possible without writing a custom navigator. Navigators need to be nested and need to have separate routes, so one tab navigator would need to be nested inside (as a tab of) the other.
Writing a custom navigator is something you definitely could consider. Here is a snack that modifies the example from react-navigation documentation:
https://snack.expo.io/#mlisik/thoughtful-stroopwafels
In the snack, the first two tabs are displayed on top, and remaining on the bottom. You would need to further modify them to match the appearance you are after with some custom options, perhaps reusing internal components from react-navigation.
It is by no means a complete solution, but should give you an idea of what is possible.
For completeness, I include the code here:
// App.js
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNavigator } from './Navigator';
const Nav = createNavigator()
const Screen1 = () => <View style={{flex: 1, backgroundColor: 'red'}} />
const Screen2 = () => <View style={{flex: 1, backgroundColor: 'green'}} />
const Screen3 = () => <View style={{flex: 1, backgroundColor: 'yellow'}} />
const Screen4 = () => <View style={{flex: 1, backgroundColor: 'brown'}} />
export default function App() {
return (
<NavigationContainer>
<Nav.Navigator>
<Nav.Screen name="Tab 1" component={Screen1} />
<Nav.Screen name="Tab 2" component={Screen2} />
<Nav.Screen name="Tab 3" component={Screen3} />
<Nav.Screen name="Tab 4" component={Screen4} />
</Nav.Navigator>
</NavigationContainer>
)
}
// Navigator.js
// this is only slightly modified from https://reactnavigation.org/docs/custom-navigators#usenavigationbuilder
import * as React from 'react';
import { Text, TouchableOpacity, View } from 'react-native';
import {
NavigationHelpersContext,
useNavigationBuilder,
createNavigatorFactory,
TabRouter,
TabActions,
} from '#react-navigation/native';
function TabButton({ route, descriptors, navigation, state }) {
return (
<TouchableOpacity
key={route.key}
onPress={() => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!event.defaultPrevented) {
navigation.dispatch({
...TabActions.jumpTo(route.name),
target: state.key,
});
}
}}
style={{ flex: 1 }}
>
<Text>{descriptors[route.key].options.title || route.name}</Text>
</TouchableOpacity>
)
}
function Navigator({
initialRouteName,
children,
screenOptions,
tabBarStyle,
contentStyle,
}) {
const { state, navigation, descriptors } = useNavigationBuilder(TabRouter, {
children,
screenOptions,
initialRouteName,
});
const renderTab = (route) => (
<TabButton
route={route}
descriptors={descriptors}
state={state}
navigation={navigation}
/>
)
return (
<NavigationHelpersContext.Provider value={navigation}>
<View style={[{ flexDirection: 'row' }, tabBarStyle]}>
{state.routes.slice(0, 2).map(renderTab)}
</View>
<View style={[{ flex: 1 }, contentStyle]}>
{descriptors[state.routes[state.index].key].render()}
</View>
<View style={[{ flexDirection: 'row' }, tabBarStyle]}>
{state.routes.slice(2).map(renderTab)}
</View>
</NavigationHelpersContext.Provider>
);
}
export const createNavigator = createNavigatorFactory(Navigator);

TypeError: undefined is not an object (evaluating props.navigation.navigate)

I have an icon on the right side of my Header. When pressed I want to be transferred to another page. However, it comes up with an error.
This is my 'icon' screen:
import React, { Component } from 'react';
import { StyleSheet, View, Text, Image, TouchableOpacity } from 'react-native';
const Login = props => {
return (
<View style={{ flexDirection: 'row' }}>
<TouchableOpacity
onPress={() => {
props.navigation.navigate({routeName: 'Login'});}}>
<Image
source={{
uri:
'https://clipartart.com/images/login-icon-clipart-5.jpg',
}}
style={{
width: 40,
height: 40,
borderRadius: 40 / 2,
marginLeft: 15,
}}
/>
</TouchableOpacity>
</View>
);
}
export default Login;
This is my 'navigation' screen:
import {createStackNavigator} from 'react-navigation-stack';
import {createAppContainer} from 'react-navigation';
import React from 'react';
import Homepage from './screens/Homepage';
import Checkoutpage from './screens/Checkoutpage';
import Filterpage from './screens/Filterpage';
import Locationpage from './screens/Locationpage';
import Menupage from './screens/MenuPage';
import Welcomepage from './screens/Welcomepage';
import Loginpage from './screens/Loginpage';
import Finalpage from './screens/Finalpage';
import Login from './Components/Login';
const Navigation = createStackNavigator({
Home:Homepage,
Checkout: Checkoutpage,
Filter: Filterpage,
Location: Locationpage,
Menu: Menupage,
Welcome: Welcomepage,
Login: Loginpage,
Final: Finalpage
},
{
defaultNavigationOptions: {
headerRight:() => <Login/>
}
}
);
I'm very new to react-native. So if you found the problem, can you please explain thoroughly so I understand. Thank you!!
So it looks like you are expecting the navigation object to be part of the props passed to your <Login/> component. This object is only defined for screen components in react-navigate.
This means that you need to get access to the navigation functionality some other way. Luckily, this library provides you with the useNavigation() hook. So using that in your component would look something like:
// react-navigation v5+
import { useNavigation } from '#react-navigation/native';
const Login = () => {
const navigation = useNavigation();
return (
<View style={{ flexDirection: "row" }}>
<TouchableOpacity
onPress={() => {
navigation.navigate({ routeName: "Login" });
}}
>
<Image
source={{
uri: "https://clipartart.com/images/login-icon-clipart-5.jpg",
}}
style={{
width: 40,
height: 40,
borderRadius: 40 / 2,
marginLeft: 15,
}}
/>
</TouchableOpacity>
</View>
);
};
It seems to me you are using React Navigation v4.x , in order to use the useNavigation hook you need to upgrade to v5.x.
The navigation prop will be passed to all screens by default and you can use the useNavigation hook like #faelks suggested (if needed in other components).
UPGRADE TO v5 FIRST.
Here you have a little example for v5.x version:
import React from 'react'
import { Button, View, StyleSheet } from 'react-native'
import { NavigationContainer } from '#react-navigation/native'
import { createStackNavigator } from '#react-navigation/stack'
const Home = ({ navigation }) => (
<View style={styles.component}>
<Button title="Go to login" onPress={() => navigation.navigate('Login')} />
</View>
)
const Login = ({ navigation }) => (
<View style={styles.component}>
<Button title="Go back" onPress={() => navigation.goBack()} />
</View>
)
const Main = createStackNavigator()
const mainConfig = {
// configuration for this stack
initialRouteName: "Home",
}
export default props => (
<NavigationContainer>
<Main.Navigator {...mainConfig}>
<Main.Screen name="Home" component={Home} />
<Main.Screen name="Login" component={Login} />
{/* Other screens for this stack */}
</Main.Navigator>
</NavigationContainer>
)
const styles = StyleSheet.create({
component: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}
})