I created a background component with a picture - react-native

I created a background component with a picture. I want to use it in Navigator for all screens but I get error Error: A navigator can only contain 'Screen', 'Group' or 'React.Fragment' as its direct children (found 'BackgroundImage'). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.
return (
<BackgroundImage>
<Stack.Navigator
screenOptions={{
headerStyle: { backgroundColor: Colors.header },
headerTintColor: 'white',
contentStyle: { backgroundColor: Colors.primary100 },
}}
>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Register" component={RegisterScreen} />
</Stack.Navigator>
</BackgroundImage>
);
}
My component
const image = require('../assets/background/home.png');
const BackgroundImage = ({children}) => (
<ImageBackground source={image} style={styles.image}>
{children}
</ImageBackground>
);
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
},
image: {
flex: 1,
justifyContent: 'center'
}
});
export default BackgroundImage;
I this case is works
function HomeScreeen() {
return (
<BackgroundImage>
<View style={styles.rootContainer}>
<Image source={logo} />
<Text style={styles.title}>Test!</Text>
</View>
</BackgroundImage>
);
}

#superDev1117 I use NavigationContener in this file and I get "Error: Looks like you have nested a 'NavigationContainer' inside another. Normally you need only one container at the root of the app, so this was probably an error. If this was intentional, pass 'independent={true}' explicitly. Note that this will make the child navigators disconnected from the parent and you won't be able to navigate between them."
function Navigation() {
const authCtx = useContext(AuthContex);
return (
<NavigationContainer>
{!authCtx.isAuthenticated && <AuthStack />}
{authCtx.isAuthenticated && <AuthenticatedStack />}
</NavigationContainer>

You can't use BackgroundImage component in this case.
Try with this:
on Root component,
<BackgroundImage>
<NavigationContainer> // from '#react-navigation/native';
<Stack.Navigator
...
/>
</NavigationContainer>
</BackgroundImage>

Related

Maximum depth exceeded: how to define a stack for a tab?

I tried following React-Navigation tutorial to implement Stack Navigator Within a Tab Navigator in React Native application using expo.
I am trying to achieve the following
RootStack (Stack Navigator)
Home (Screen)
Login (Screen)
ConsumerApp (Tab Navigator)
SettingsStackScreen (Stack Navigator)
Settings (Screen)
Details (Screen)
BusinessApp
Orders (screen)
Even when following the tutorial and using the code defined there (as you can see from SettingsStackScreen) I am getting the following error printed over and over again. The BusinessApp works great, because it doesn't have Stack Navigators defined within it, only pure component screen.
ERROR Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
in PreventRemoveProvider (created by NavigationContent)
in NavigationContent
in Unknown (created by NativeStackNavigator)
in NativeStackNavigator (created by SettingsStackScreen)
in SettingsStackScreen (created by SceneView)
I tried creating the most minimal example possible, so I wasn't sure what else to change/try.
This is root stack navigator in App.js
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} options={{ headerShown: false }} />
<Stack.Screen name="Login" component={LoginScreen} options={{ title: t('Login-s') }}/>
<Stack.Screen name="ConsumerApp" component={ConsumerAppScreen} options={{headerShown: false}}/>
<Stack.Screen name="BusinessApp" component={BusinessAppScreen} options={{headerShown: false}}/>
</Stack.Navigator>
</NavigationContainer>
);
}
The ConsumerAppScreen:
const Tab = createBottomTabNavigator();
export const ConsumerAppScreen = () => {
const { t, i18n } = useTranslation();
function DetailsScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Details!</Text>
</View>
);
}
function SettingsScreen({ 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 SettingsStack = createNativeStackNavigator();
function SettingsStackScreen() {
return (
<SettingsStack.Navigator>
<SettingsStack.Screen name="Settings" component={SettingsScreen} />
<SettingsStack.Screen name="Details" component={DetailsScreen} />
</SettingsStack.Navigator>
);
}
return (
<Tab.Navigator
initialRouteName="Settings"
tabBar={(props) => <ConsumerTabBar {...props} />}
>
<Tab.Screen name="Settings" component={SettingsStackScreen} options={{ icon: require("../../assets/dashboard-icon.png"), title: t('Settings') }} />
</Tab.Navigator>
)
}
It's because the way you created ConsumerAppScreen is not correct. Screen components and StackNavigator declarations shouldn't be inside a react component.
(I think the tab bar should have more than one child, but I'm not sure about it.)
The correct implementation:
function DetailsScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Details!</Text>
</View>
);
}
function SettingsScreen({ 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 SettingsStack = createNativeStackNavigator();
function SettingsStackScreen() {
return (
<SettingsStack.Navigator>
<SettingsStack.Screen name="Settings" component={SettingsScreen} />
<SettingsStack.Screen name="Details" component={DetailsScreen} />
</SettingsStack.Navigator>
);
}
// tab navigation
const Tab = createBottomTabNavigator();
export const ConsumerAppScreen = () => {
const { t, i18n } = useTranslation();
return (
<Tab.Navigator
initialRouteName="Settings"
tabBar={(props) => <ConsumerTabBar {...props} />}
>
<Tab.Screen name="Settings" component={SettingsStackScreen} options={{ icon: require("../../assets/dashboard-icon.png"), title: t('Settings') }} />
</Tab.Navigator>
)
}

React native : bottom navigation with dynamic initialRouteName implementation

I am a beginner in react native. I have gone through different related topics. But failed. This is my issues,
I have a bottom navigator with 4 items, say Dashboard, X, Patient and Y. Here is the optimised bottom navigator code.
const Stack = createStackNavigator();
const Bottom = createBottomTabNavigator();
const Main = () => {
return (
<Bottom.Navigator
initialRouteName="DashboardScreenStack"
tabBarOptions={{
style: {
height: 70,
paddingTop: 20,
backgroundColor: '#F3F6FF',
},
activeTintColor: colors.navigationTextActive,
inactiveTintColor: colors.navigationTextInactive,
labelStyle: {
fontSize: 15,
marginTop: 15,
paddingBottom: 10,
},
}}>
<Bottom.Screen
name="DashboardScreenStack"
component={DashboardScreenStack}
options={{
tabBarLabel: 'Dashboard',
}}
/>
<Bottom.Screen
name="X"
component={X}
options={{
tabBarLabel: 'X',
}}
/>
<Bottom.Screen
name="Patient"
component={Patient}
options={{
tabBarLabel: 'Patient',
}}
/>
<Bottom.Screen
name="Y"
component={Y}
options={{
tabBarLabel: 'Y',
}}
/>
</Bottom.Navigator>
);
};
This is my code for Patient menu.
const Patient = (props) => {
let resultData = null;
var initialRoute = '';
if (
typeof props.route.params != 'undefined' &&
props.route.params.result != null
) {
resultData = props.route.params.result;
initialRoute = 'PatientDashboardScreen';
} else {
initialRoute = 'AddPatientScreen';
}
return (
<Stack.Navigator
initialRouteName={initialRoute }>
<Stack.Screen
name="PatientDashboardScreen"
component={PatientDashboardScreen}
initialParams={resultData}
options={{headerShown: false}}
/>
<Stack.Screen
name="TestScreen1"
component={TestScreen1}
options={{headerShown: false}}
/>
<Stack.Screen
name="TestScreen2"
component={TestScreen2}
options={{headerShown: false}}
/>
<Stack.Screen
name="AddPatientScreen"
component={AddPatientScreen}
options={{headerShown: false}}
/>
</Stack.Navigator>
);
};
There are 4 screens that should be shown in Patient menu. Out of those if I am selecting an item in my Dashboard menu I need to open "PatientDashboardScreen". And there will be some data available in props too. But on directly clicking 'Patient' menu, I need to move to "AddPatientScreen" where no data is passed.
I tried the above code. But only the initial click works. If I am selecting from list first, the always Patient menu is showing "PatientDashboardScreen" and if I am selecting Patient menu directly first, then always "AddPatientScreen" is shown on Patient menu selection.
Any help would be greateful. Thank you
Based on your question
You have a bottom navigator and one of the screens has a nested stack navigator.
The requirement here is to show a specific screen when pressing the bottom navigator button and redirect to a screen when opening from another screen in bottom navigator along with some parameters.
This is one way to do this.
<Tab.Screen
name="Hospital"
component={HospitalView}
options={({ navigation }) => ({
tabBarButton: (props) => (
<TouchableOpacity
{...props}
onPress={() =>
navigation.navigate('Hospital', { screen: 'Patient' })
}
/>
),
})}
/>
You can have a custom onPress in your bottom navigator button which will use the navigate with the screen option which will take you to the specific screen.
To navigate from another screen with parameters you can use the below option
<Button
title="Doctor"
onPress={() =>
navigation.navigate('Hospital', {
screen: 'Doctor',
params: { name: 'Doc 1' },
})
}
/>
Your full code should look something similar to this
const Stack = createStackNavigator();
function Patient() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Patient Screen</Text>
</View>
);
}
function Doctor({route}) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Doctor Screen</Text>
<Text>{route?.params?.name}</Text>
</View>
);
}
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home!</Text>
<Button
title="Doctor"
onPress={() =>
navigation.navigate('Hospital', {
screen: 'Doctor',
params: { name: 'Doc 1' },
})
}
/>
</View>
);
}
function HospitalView() {
return (
<Stack.Navigator>
<Stack.Screen name="Doctor" component={Doctor} />
<Stack.Screen name="Patient" component={Patient} />
</Stack.Navigator>
);
}
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen
name="Hospital"
component={HospitalView}
options={({ navigation }) => ({
tabBarButton: (props) => (
<TouchableOpacity
{...props}
onPress={() =>
navigation.navigate('Hospital', { screen: 'Patient' })
}
/>
),
})}
/>
</Tab.Navigator>
);
}
You can refer this sample
https://snack.expo.io/#guruparan/5f3f1d

Pass props though screenstack

Im trying to pass some props to though an screenstack element in react native. I have a button which onPress will ask for a screen using react navigation like this:
<Button
title="Lees Meer"
color="#d10a10"
onPress={() => RootNavigation.navigate('Article', {
params: { title: title, text: text, image: image },
})}
/>
I want thos params to be used in the article to fill in the text, title and image. I thought I could just use them like this in the article:
function ArticleFull({ navigation, params }) {
return (
<View>
<Header/>
<Card>
<CardItem header bordered>
<Body>
<Image
style={{ width: '100%', height: 400 }}
source={{ uri: 'https://www.holland.com/upload_mm/9/a/b/68638_fullimage_zwolle_sassenpoort.jpg' }}
/>
<Text>
{params.text}
</Text>
</Body>
</CardItem>
</Card>
<Button
title="Go Back"
color="#d10a10"
width= "10%"
onPress={() => navigation.goBack()}
/>
</View>
);
}
export default ArticleFull;
In the app.js i made these screenstacks which is used to navigate to an article but i need it to contain some params which are set in the homepage using the button.
const Stack = createStackNavigator();
App.js:
export default class App extends Component {
render() {
return (
<NavigationContainer ref={navigationRef}>
<Stack.Navigator initialRouteName="Home" >
<Stack.Screen options={{headerShown: false}} name="Home" component={HomePage} />
<Stack.Screen options={{headerShown: false}} name="Article" component={ArticleFull} />
</Stack.Navigator>
</NavigationContainer>
);
}
}
You can pass like this:-
<Button
title="Go to Details"
onPress={() => {
/* 1. Navigate to the Details route with params */
navigation.navigate('Details', {
itemId: 86,
otherParam: 'anything you want here',
});
}}
/>
and receive like this:-
function DetailsScreen({ route, navigation }) {
/* 2. Get the param */
const { itemId } = route.params;
const { otherParam } = route.params;
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>itemId: {JSON.stringify(itemId)}</Text>
<Text>otherParam: {JSON.stringify(otherParam)}</Text>
);
}
source
Hope it helps!!!

How to set Android Header as full width image in React Native

How do I get the image to fill the appbar. I created a custom component as below:
class HeaderImage extends React.Component {
render() {
return (
<View style={styles.imageWrapper}>
<Image style={styles.image}
resizeMode={'cover'}
source={require('../../assets/images/header1.jpg')}/>
</View>
);
}
}
const styles = StyleSheet.create({
imageWrapper: {
backgroundColor:"red",
flex: 1,
alignItems: 'stretch',
height:120
},
image: {
flex: 1,
},
});
export default HeaderImage;
Then I added it to the StackNavigator
<Stack.Screen name="Dashboard" component={Dashboard}
options={{headerTitle: props => <HeaderImage {...props} />}}
/>
But it doesnt fill the width.It just looks like its floating
You should use headerBackground to cover the whole app bar.
// HeaderImage.js
export class HeaderImage extends React.Component {
render() {
return (
<Image
style={{ height: "100%", flex: 1 }}
source={require('../../assets/images/header1.jpg')}
/>
);
}
}
// Navigation.js
// ...
<Stack.Screen
name="Dashboard"
component={Dashboard}
options={{
headerBackground: () => <HeaderImage />
}}
/>
// ...

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;