React-Navigation: How to implement multi-independant viewstacks with react-navigation (React-Native) - react-native

Good morning, good afternoon or good evening
I want to archive this navigation interface (for desktop / tablet screen size):
https://photos.app.goo.gl/ZEna1uozQP1UnVWm6
Each viewstack should be fully dependent to only one navigation; I am using react-navigation.
However, with my following code, the the pane view (the card) is completely hidden / gone on the main page.
How should I do this in order to archive the results on the prototype / video.
Below are my codes.
Thank you!
App.js (Main navigation)
import React, { FC } from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { ThemeProvider } from '#emotion/react';
import setTheme from '#lib/theme';
import changeNavigationBarColor from 'react-native-navigation-bar-color';
import {
Home as Billing,
Customers,
} from '#components/5-pages';
const theme = setTheme('light');
const Stack = createStackNavigator();
changeNavigationBarColor("black", false, true);
type Props = {};
const App: FC<Props> = ({...props}) => {
return (
<ThemeProvider theme={theme}>
<NavigationContainer>
<Stack.Navigator initialRouteName={"billing"} screenOptions={{ headerShown: false }}>
<Stack.Screen name="billing" component={ Billing } />
<Stack.Screen name="customers" component={ Customers } />
</Stack.Navigator>
</NavigationContainer>
</ThemeProvider>
);
};
export default App;
Card.js (Pane / Inner navigation)
import React, { FC }from 'react';
import styled from '#emotion/native';
import Text from './Text';
import setTheme from '#lib/theme';
import { NavigationContainer, useNavigation } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { ThemeProvider } from '#emotion/react';
const Container = styled.View`
background: ${({ theme }: any) => theme.colors.white };
border-radius: 6px;
flex-direction: column;
padding: 6px 12px;
margin: 6px 12px;
width: 320px;
`;
const Content = styled.View`
align-items: center;
flex-direction: column;
padding-left: 6px;
`;
const Title = styled(Text)`
font-size: 15px;
font-weight: bold;
color: #404040;
line-height: 49px;
`;
const Stack = createStackNavigator();
const theme = setTheme('light');
const Card: FC<Props> = ({ children, modal, title, ...props }) => {
const List : FC<Props>= () => {
const navigation = useNavigation();
return (
<Container {...props} >
<Title>{title}</Title>
<Content onModal={()=>{navigation.push("modal", props)}}>
{children}
</Content>
</Container>
)};
const Modal : FC<Props>= () => {
return (
<Container {...props} >
<Title>{title}</Title>
<Content>
{modal}
</Content>
</Container>
)};
return (
<NavigationContainer independent={true}>
<Stack.Navigator initialRouteName={"list"} screenOptions={{ headerShown: false }}>
<Stack.Screen name="list" component={ List } />
<Stack.Screen name="modal" component={ Modal } />
</Stack.Navigator>
</NavigationContainer>
);
}
type Props = {
title?: string;
children?: React.ReactNode;
modal?: React.ReactNode;
};
export default Card;

Related

How to add tabs and show the name of the button in the header of the application?

How to add submenu or tabs in the upper part in connection with the lower button and show the name of the lower button in the header of the application.
import React from 'react';
import { createStackNavigator } from 'react-navigation-stack';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import { createAppContainer } from 'react-navigation';
import { MaterialIcons, MaterialCommunityIcons } from '#expo/vector-icons';
import Splash from '../screens/Splash';
import NewsListScreen from '../screens/NewsListScreen';
import NewsItemScreen from '../screens/NewsItemScreen';
const StackNavigator = createStackNavigator({
//Splash: {screen: Splash},
News: {
screen: NewsListScreen
},
NewsItem: {
screen: NewsItemScreen,
navigationOptions: {
headerTitle: 'News Item'
}
}
});
const BottomTabNavigator = createBottomTabNavigator({
Home: {
screen: StackNavigator,
navigationOptions: {
tabBarIcon: () => <MaterialIcons name="home" size={24} />
}
},
News: {
screen: StackNavigator,
navigationOptions: {
tabBarIcon: () => <MaterialCommunityIcons name="newspaper-variant-outline" size={24} />
}
}
})
export default createAppContainer(BottomTabNavigator);
What I want to achieve is the following:
As you can see, the bottom button [News] has three referential buttons in the upper part [FEATURED], [RELEVANT], [SEARCH] and, in addition to that, it recovers the name of the bottom button and adds it to the application header below the top buttons.
[EDITED]
Your NewsListScreen screen, instead of just being a tab bar component, can be something like that:
function NewsListScreen = (props) => (
<View>
<Text>News</Text>
<TabBarComponent {...props} />
</View>
)
function TabBarComponent = createMaterialTopTabNavigator({
featured: ... ,
relevant: ... ,
search: ... ,
})
That being said, if you can, you should update your project to react navigation v5, which is I think much more user friendly.
Final Output:
Implementation using react-navigation v5 :
import * as React from 'react';
import { Text, View, StyleSheet, Dimensions } from 'react-native';
import Constants from 'expo-constants';
import {
NavigationContainer,
useNavigationState,
} from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { createMaterialTopTabNavigator } from '#react-navigation/material-top-tabs';
const TopTab = createMaterialTopTabNavigator();
const BottomTab = createBottomTabNavigator();
/*
Here I am using MaterialTopTabNavigator instead of StackNavigator,
which is a better choice based on UI snapshot in this case.
*/
function NewsTopTabs() {
/*
in case you want to set the title dynamically then you can use the
useNavigationState hook but as there are just two bottom tabs so
I am using hardcoded values.
*/
const nav = useNavigationState((state) => state);
return (
<>
<View style={styles.titleBar}>
<Text style={styles.title}>News</Text>
</View>
<TopTab.Navigator>
<TopTab.Screen name="Featured" component={Featured} />
<TopTab.Screen name="Relevant" component={Relevant} />
<TopTab.Screen name="Search" component={Search} />
</TopTab.Navigator>
</>
);
}
function MatchesTopTabs() {
return (
<>
<View style={styles.titleBar}>
<Text style={styles.title}>Matches</Text>
</View>
<TopTab.Navigator>
<TopTab.Screen name="Featured" component={Featured} />
<TopTab.Screen name="Relevant" component={Relevant} />
<TopTab.Screen name="Search" component={Search} />
</TopTab.Navigator>
</>
);
}
function MyBottomTabs({ navigation }) {
return (
<BottomTab.Navigator>
<BottomTab.Screen name="News" component={NewsTopTabs} />
<BottomTab.Screen name="Matches" component={MatchesTopTabs} />
</BottomTab.Navigator>
);
}
//[FEATURED], [RELEVANT], [SEARCH]
export default function App() {
return (
<NavigationContainer>
<MyBottomTabs />
</NavigationContainer>
);
}
//[FEATURED], [RELEVANT], [SEARCH]
const Featured = () => {
const nav = useNavigationState((state) => state);
return (
<View style={styles.screen}>
<Text>Featured</Text>
</View>
);
};
const Relevant = () => {
const nav = useNavigationState((state) => state);
return (
<View style={styles.screen}>
<Text>Relevant</Text>
</View>
);
};
const Search = () => {
const nav = useNavigationState((state) => state);
return (
<View style={styles.screen}>
<Text>Search</Text>
</View>
);
};
const styles = StyleSheet.create({
titleBar: {
height: 50,
width: Math.round(Dimensions.get('window').width),
backgroundColor: 'white',
justifyContent: 'center',
borderBottomColor: "lightgrey",
borderBottomWidth: 1,
paddingLeft: 10,
},
title: {
fontSize: 20,
fontWeight: 'bold',
},
screen:{
flex: 1,
justifyContent: "center",
alignItems: "center"
}
});
Working Example: Expo Snack

How to pass data through stack navigation to a specific screen on react native tabs?

I'm creating a bottom tabs in react native, which will have two screens Home and News.
But first, user will need to Sign In and the users data will be passed from the login screen to Home screen. How do i pass those data. By using
navigation.navigate('Home', {Name: Name});
I can successfuly retrieve the data in Homescreen, if I just use two screen(Login and Home in a stack). However, when I change to navigate to the tabs(which includes Home and News), it doesnt work with error 'Undefined is not an object(evaluating 'route.params.Name'.
May you guys show me which part did I miss?
Here's the app.js code.
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import HomeScreen from './homescreen';
import NewsScreen from './newsscreen';
import LoginScreen from './loginscreen';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { createStackNavigator } from '#react-navigation/stack';
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="News" component={NewsScreen} />
</Tab.Navigator>
);
}
const LoginStack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<LoginStack.Navigator screenOptions={{headerShown: false}}
initialRouteName="Login">
<LoginStack.Screen name="Login"component={LoginScreen}/>
<LoginStack.Screen name="MyTabs" component={MyTabs} />
</LoginStack.Navigator>
</NavigationContainer>
);
}
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Following is the homescreen code:
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default function HomeScreen({route, navigation}) {
var Name = route.params.Name;
return (
<View style={styles.container}>
<Text>{Name}</Text>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
And finally here's the login code:
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
export default function LoginScreen({navigation}) {
const Name = 'Boy';
const login = () => {
navigation.navigate('MyTabs', {Name: 'Boy'});}
return (
<View style={styles.container}>
<Text>LoginScreen</Text>
<TouchableOpacity
onPress={login}
><Text
>LOGIN</Text>
</TouchableOpacity>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
})
I'm trying to learn how to pass data from a screen to another screen, in which the screen is located inside a tab stack. I hope you guys can understand the question and provide me with your opinion and solution. Thanks.
Output:
It needed just this little modification in MyTabs component:
function MyTabs({ navigation, route }) {
const { name } = route.params;
console.log(name);
return (
<Tab.Navigator>
<Tab.Screen
name="Home"
component={() => <HomeScreen name={route.params.name} />}
/>
<Tab.Screen name="News" component={NewsScreen} />
</Tab.Navigator>
);
}
Here is the working solution:
App.js
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
import HomeScreen from './home';
import NewsScreen from './newscreen';
import LoginScreen from './login';
// You can import from local files
const Tab = createBottomTabNavigator();
function MyTabs({ navigation, route }) {
const { name } = route.params;
console.log(name);
return (
<Tab.Navigator>
<Tab.Screen
name="Home"
component={() => <HomeScreen name={route.params.name} />}
/>
<Tab.Screen name="News" component={NewsScreen} />
</Tab.Navigator>
);
}
const LoginStack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<LoginStack.Navigator
screenOptions={{ headerShown: true }}
initialRouteName="Login">
<LoginStack.Screen name="Login" component={LoginScreen} />
<LoginStack.Screen name="MyTabs" component={MyTabs} />
</LoginStack.Navigator>
</NavigationContainer>
);
}
export default App;
LoginScreen
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import React from "react";
export default function LoginScreen({ navigation }) {
const Name = 'Name From Login Screen';
const login = () => {
console.log("hi");
navigation.navigate('MyTabs', { name : Name });
}
return (
<View style={styles.container}>
<View style={styles.bottomView}>
<TouchableOpacity onPress={login} style={styles.button}>
<Text style={styles.textStyle}>LOGIN</Text>
</TouchableOpacity>
</View>
</View>
);
}
Home.js
import { Text, View, StyleSheet } from 'react-native';
import React from "react";
export default function HomeScreen({route, navigation, name}) {
console.log("***",name)
return (
<View style={styles.container}>
<Text style={styles.name}>{name}</Text>
</View>
);
}
Working Expo Snack example.
It's a little complicated to explain react native screens once you start combining bottomtabnavigator and stack navigator and even drawernavigation.
you may need to create stack navigation inside tab navigation like this.
//creating a separate stack within your tab
const HomeStack = createStackNavigator()
const HomeStackNavigator = () => {
return (
<HomeStack.Navigator screenOptions={{headerShown: false}}
initialRouteName="HomeScreen">
<HomeStack.Screen name="Home"component={HomeScreen}/>
</HomeStack.Navigator>
)
}
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Tab.Navigator initialRouteName="HomeStackNavigator">
<Tab.Screen name="HomeStackNavigator" component={HomeStackNavigator} />
<Tab.Screen name="News" component={NewsScreen} />
</Tab.Navigator>
);
}
I believe, different navigators doesn't really talk to each other well. If you are using different navigator be prepare to nest stack navigators inside them.
The idea is, the parent of each display component should be a stack navigator. This will also allow you to better control your screenOptions.

React Navigation Bottom Tabs occupying the whole screen in React Native Web

I'm trying to use React-Navigation bottom tabs in React-Native-Web, the result is the expected in the device:
result in device
But in the web it gets like this:
result in the browser
If I inspect the page I can find the elements of main in there.
and here is the code:
src/App.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* #format
* #flow strict-local
*/
import React from 'react';
import {StatusBar} from 'react-native';
import {NavigationContainer} from '#react-navigation/native';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import Main from './pages/Main/index';
const Tab = createBottomTabNavigator();
const App = () => {
return (
<>
<StatusBar
backgroundColor="transparent"
translucent
barStyle="light-content"
/>
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Main" component={Main} />
</Tab.Navigator>
</NavigationContainer>
</>
);
};
export default App;
src/pages/Main/index.js
import React, {useState, useEffect} from 'react';
import {View, Dimensions} from 'react-native';
// import api from '../../services/api';
import Company from '../../components/Company';
import {Container, Title, List} from './styles';
export default function Main() {
//...
return (
<View
style={{
display: 'flex',
flexDirection: 'column',
height: Dimensions.get('window').height,
justifyContent: 'center',
}}>
<Container>
<Title>Empresas Cadastradas</Title>
<List
keyboardShoulPersistTaps="handled"
data={companies}
keyExtractor={(item) => String(item.id)}
renderItem={({item}) => <Company data={item} />}
/>
</Container>
</View>
);
}
src/pages/Main/styles.js
import styled from 'styled-components/native';
export const Container = styled.View`
background-color: #7159c1;
padding-top: 30px;
flex: 1;
`;
export const Title = styled.Text`
font-size: 32px;
color: #fff;
font-weight: bold;
padding: 0 20px;
margin-bottom: 10px;
`;
export const List = styled.FlatList.attrs({
contentContainerStyle: {paddingHorizontal: 10},
showsVerticalScrollIndicator: false,
})`
margin-top: 20px;
`;
I've encountered the same problem, and the solution is to add index.css somewhere in your src, load it in your index.js or index.web.js, then put this:
html,
body {
height: 100%;
}
/* These styles disable body scrolling if you are using <ScrollView> */
body {
overflow: hidden;
}
/* These styles make the root element full-height */
#root {
display: flex;
height: 100%;
}
Found the solution in the following discussion:
https://github.com/react-navigation/react-navigation/issues/9187

onPress event not working on TouchableOpacity on Android

Before I begin, I have checked out other common solutions to this problem, like importing TouchableOpacity from 'react-native' instead of 'react-gesture-handler' (I don't even have it installed), or changing z-index/absolute positioning. None of them work.
I'm a beginner in react native. I'm trying to follow along with a react native course, but I'm stuck on this issue. I'm using Expo on a real Android. I don't use emulators. Haven't tested on iOS as I don't have access to one. When I add onPress to TouchableOpacity, most of the time it gives feedback when clicked (opacity changes), but it won't fire the onPress event. Sometimes, however, it works. When I press on the right edge, sometimes the event fires. Here's an example:
index.js
import React from "react";
import Options from "./screens/Options";
export default function App() {
return <Options />;
}
Options.js
import React from "react";
import { View, StyleSheet } from "react-native";
import RowItem from "../components/RowItem";
import Constants from "expo-constants";
import styled from "styled-components";
import colors from "../constants/colors";
import { Entypo } from "#expo/vector-icons";
const SSeparator = styled.View`
background: ${colors.border};
height: ${StyleSheet.hairlineWidth}px;
margin-left: 20px;
`;
const SView = styled.View`
margin-top: ${Constants.statusBarHeight}px;
`;
export default () => {
return (
<SView>
<RowItem
title="Themes"
icon={
<Entypo
name="chevron-left"
size={20}
color={colors.blue}
onPress={() => alert("click")}
/>
}
/>
<SSeparator />
</SView>
);
};
RowItem.js
import React from "react";
import { TouchableOpacity, Text } from "react-native";
import styled from "styled-components";
import colors from "../constants/colors";
const SText = styled.Text`
font-size: 16px;
`;
const STouchableOpacity = styled.TouchableOpacity`
margin: 16px 20px;
flex-direction: row;
align-items: center;
justify-content: space-between;
`;
export default ({ title, icon, onPress }) => {
return (
<STouchableOpacity onPress={onPress}>
<SText>{title}</SText>
{icon}
</STouchableOpacity>
);
};
Any ideas?
can you try like this:
export default () => {
const testFunc = () =>{
alert('test')
}
return (
<SView>
<RowItem
title="Themes"
icon={
<Entypo
name="chevron-left"
size={20}
color={colors.blue}
onPress={testFunc}
/>
}
/>
<SSeparator />
</SView>
);
};
and then :
<STouchableOpacity onPress={()=>onPress()}>
<SText>{title}</SText>
{icon}
</STouchableOpacity>
Okay, so I finally noticed the mistake, and I feel ashamed for not noticing it. I kept reading the code but I couldn't see the obvious.
I mistakenly copied the onPress event into the icon instead of putting it directly inside RowItem.
Instead of this:
<RowItem
title="Themes"
icon={
<Entypo
name="chevron-left"
size={20}
color={colors.blue}
onPress={() => alert("click")}
/>
}
/>
It should be this:
<RowItem
title="Themes"
handlePress={() => alert("test")}
icon={<Entypo name="chevron-left" size={20} color={colors.blue} />}
/>
And inside RowItem.js:
export default ({ title, icon, handlePress }) => {
return (
<STouchableOpacity onPress={() => handlePress()}>
<Text>{title}</Text>
{icon}
</STouchableOpacity>
);
};
And now the app works as intended.

Webview&redabilitywebview works fine in android not in ios react native

I am using a inapp browser using webview and readbilitywebview. it works fine in android but in ios the webview dosent display any content other than title of news and in readbilitywebview getting an error "invariant violation require native component rncwkwebview manager not found in the ui manager"
Using expo cli for development.
Webview:
import React, { Component } from 'react';
import { WebView,StyleSheet,View } from 'react-native';
import { Container, Text } from 'native-base';
import { Ionicons } from '#expo/vector-icons';
import { Header } from 'react-native-elements';
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import { NavigationActions, StackActions } from 'react-navigation';
import SimpleWebView from './webModal';
class MyWeb extends Component {
render() {
const { navigation } = this.props;
const title = JSON.stringify(navigation.getParam('title'))
const url = (navigation.getParam('url'))
return (
<View style={styles.container}>
<Header
backgroundColor="white"
/*leftComponent={<Ionicons name="md-arrow-round-back"
size={25}
onPress={() => {
alert('You tapped the button!');
}}/>}*/
centerComponent={
<Text
numberOfLines={1}
style={{
fontSize: 14,
fontWeight: 'bold'
}}>
{title}
</Text>}
rightComponent={
<Ionicons name="ios-book"
size={25}
onPress={() => {
this.props.navigation.navigate('Webview', {
url: (url),
title: (title)
});
}} />
}
/>
<WebView
source={{
uri: (navigation.getParam('url'))
}}
style={{ marginTop: 20 }}
/>
</View>
);
}
}
const MainNavigator = createStackNavigator({
Home: {
screen: MyWeb,
navigationOptions: {
header: null,
},
},
Webview: {
screen: SimpleWebView,
navigationOptions: {
header: null,
},
},
},
{
initialRouteName: 'Home',
}
);
const styles = StyleSheet.create({
container: {
flex: 1,
}
});
const Webpage = createAppContainer(MainNavigator);
export default Webpage;
Redabilitywebview
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import ReadabilityWebView from "react-native-webview-readability";
import Constants from 'expo-constants';
const css =
`body {
color: #2a2a2a;
}
img, figure {
display: none;
}
h1 {
border-bottom-width: 1px;
border-color: #ccc;
padding-bottom: 3px;
border-bottom-style:solid;
font-size: 1.6em;
font-weight: bold;
letter-spacing: .05em;
}
p {
letter-spacing: .03em;
}`;
export default class SimpleWebView extends React.Component {
render() {
const { navigation } = this.props;
return (
<View style={styles.continer}>
<ReadabilityWebView
automaticallyAdjustContentInsets={true}
htmlCss={css}
url={(navigation.getParam('url'))}
title=
{navigation.getParam('title')}
/>
</View>
);
}
}
const styles = StyleSheet.create({
continer: {
flex: 1,
paddingTop: Constants.statusBarHeight
}
});
workes in android but not in ios. Iam using expo cli.