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 />
}}
/>
// ...
Related
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>
So I'm following along with this tutorial trying to make an app with react native and it uses v3 of react-navigation but I'm trying to use v5. The issue is with this the styling that was previously working in the old version no longer does.
**// setup of navigator**
const Stack = createStackNavigator();
const Travel = () => {
return (
<Stack.Navigator initialRouteName="List">
<Stack.Screen name="Article" component={Article} />
<Stack.Screen
name="List"
component={List}
options={{
title: null,
}}
/>
</Stack.Navigator>
);
};
**// screen in question - List**
const styles = StyleSheet.create({
flex: {
flex: 1,
},
row: {
flexDirection: 'row',
},
header: {
backgroundColor: 'orange',
},
});
export default class List extends React.Component {
static navigationOptions = {
header: (
<View style={[styles.flex, styles.row, styles.header]}> **// styles won't get applied for some reason**
<View>
<Text>Search for a place</Text>
<Text>Destination</Text>
</View>
<View>
<Text>Avatars</Text>
</View>
</View>
),
};
render() {
return <Text>List</Text>;
}
}
I'm at a loss to make this work and look like this: goal navigation
Any idea how to update this code from this v3 version in the tutorial to the v5 support version of react-navigation?
UPDATE:
I've gotten closer to the goal with this code:
import React from 'react';
import { createStackNavigator } from '#react-navigation/stack';
import { Text, View } from 'react-native';
import Article from '../screens/Article';
import { List } from '../screens/List';
const Stack = createStackNavigator();
const ListHead = () => {
return (
<View
style={{
flex: 1,
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: 'orange',
}}
>
<View>
<Text>Search for a place</Text>
<Text>Destination</Text>
</View>
<View>
<Text>Avatars</Text>
</View>
</View>
);
};
const Travel = () => {
return (
<Stack.Navigator initialRouteName="List">
<Stack.Screen name="Article" component={Article} />
<Stack.Screen
name="List"
component={List}
options={{
headerTitle: () => <ListHead />,
}}
/>
</Stack.Navigator>
);
};
export default Travel;
However I can't get these to properly be spaced apart like the goal photo and they don't take up the full width.
In react-navigation v5 you can use headerLeft and headerRight for a custom navigation header like this. You can also specify the styling according to your needs.
Here is the code example
export default class List extends React.Component {
static navigationOptions = {
headerLeft: () => (
<View>
<Text>Search for a place</Text>
<Text>Destination</Text>
</View>
),
headerRight: () => (
<View>
<Text>Avatars</Text>
</View>
),
// you can specify your styles here also
headerStyle: {},
headerRightContainerStyle: {},
headerLeftContainerStyle: {},
};
render() {
return <Text>List</Text>;
}
}
I hope this will help you out.!
I have used createMaterialBottomTabNavigator and redux to change badge text. Here is the code:
class Router extends React.PureComponent {
constructor(props) {
super(props);
this.state = {}
}
componentDidMount() {
this.props.appAct()
this.props.getCartAct()
}
HomeStack = () => (
<Stack.Navigator headerMode={'none'}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Product" component={ProductStack} />
</Stack.Navigator>
)
CartStack = () => (
<Stack.Navigator headerMode={'none'}>
<Stack.Screen name="Cart" component={CartScreen} />
<Stack.Screen name="Product" component={ProductStack} />
</Stack.Navigator>
)
render() {
let { internet, showIntro } = this.props;
if (!internet) return <NoInternetScreen />
if (showIntro) return <AppIntro />
return (
<NavigationContainer ref={navigationRef}>
<Tab.Navigator
style={{ backgroundColor: "transparent", overflow: "hidden" }}
initialRouteName="Home"
activeColor="#ac85f2"
inactiveColor={Colors.dark50}
barStyle={{
borderColor: Colors.dark70,
overflow: 'hidden',
borderTopWidth: 1,
borderLeftWidth: 1,
borderRightWidth: 1,
backgroundColor: "#eee"
}}
>
<Tab.Screen
name="Home"
component={HomeStack}
options={{
tabBarLabel: <Text default> home </Text>,
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons name="home" color={color} size={26} />
),
}}
/>
<Tab.Screen
name="Cart"
component={CartStack}
options={({ route }) =>
({
tabBarLabel: <Text default> cart </Text>,
tabBarBadge: props.cart.length, // <= here it changes the badge text
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons name="cart-outline" color={color} size={26} />
),
})
}
/>
</Tab.Navigator>
</NavigationContainer>
)
}
}
function mapStateToProps(state) {
return {
internet: state.appReducer.internet,
showIntro: state.appReducer.showIntro,
cart: state.appReducer.cart,
}
}
function mapDispatchToProps(dispatch) {
return {
appAct: () => dispatch(appAction()),
getCartAct: () => dispatch(getCartAction()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Router)
The problem is when I try to change cart prop from an action, the whole components re-render. For instance this is my HomeScreen component which re-renders when tabBarBadge changes:
import _ from 'lodash';
import React from 'react';
import { StyleSheet, ScrollView, Image, TouchableOpacity } from 'react-native';
import { Constants, Spacings, View, Text, Carousel, Colors, Button, Card } from 'react-native-ui-lib';
import { connect } from 'react-redux'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import { homeAction } from '../actions'
import Loaders from '../components/loaders'
import { HomeCats } from '../components/HomeCats';
const INITIAL_PAGE = 2;
const IMAGES = [
require('../assets/images/s1.jpg'),
require('../assets/images/s2.jpg'),
require('../assets/images/s3.jpg'),
];
const image = require("../assets/images/b2.jpg")
class HomeScreen extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
orientation: Constants.orientation,
width: this.getWidth(),
limitShownPages: false,
numberOfPagesShown: 7,
currentPage: INITIAL_PAGE,
autoplay: false
}
}
componentDidMount() {
console.log("componentDidMount")
console.log('this.props.categories.length', this.props.categories.length)
if (this.props.categories.length < 1) {
this.props.getCategoriesAct()
}
}
getWidth = () => {
return Constants.windowWidth - Spacings.s5 * 2;
}
onChangePage = (currentPage) => {
this.setState({ currentPage });
}
onPagePress = (index) => {
this.carousel.goToPage(index, true);
}
render() {
const { limitShownPages, numberOfPagesShown, autoplay, width } = this.state;
const { loadingCats, categories } = this.props;
return (
<ScrollView showsVerticalScrollIndicator={false} >
<View marginV-20 >
<View marginB-15>
<Carousel
key={numberOfPagesShown}
migrate
ref={(r) => (this.carousel = r)}
//loop
autoplay={autoplay}
onChangePage={this.onChangePage}
pageWidth={width}
itemSpacings={Spacings.s3}
containerMarginHorizontal={Spacings.s2}
initialPage={INITIAL_PAGE}
containerStyle={{ height: 180, }}
containerPaddingVertical={18}
pageControlPosition={'over'}
pageControlProps={{ onPagePress: this.onPagePress, limitShownPages }}
allowAccessibleLayout
>
{
IMAGES.map((image, i) =>
<View flex centerV br20 key={i} style={{
shadowColor: "#eee",
shadowOffset: {
width: 0,
height: 5,
},
shadowOpacity: 0.09,
shadowRadius: 1,
elevation: 15,
}}>
<Image
imageStyle="contain"
style={{ flex: 1, borderRadius: 20, width: "100%" }}
source={image}
/>
</View>
)
}
</Carousel>
</View>
</View>
</ScrollView>
)
}
}
function mapStateToProps(state) {
return {
error: state.homeReducer.error,
loadingCats: state.categoryReducer.loading,
categories: state.categoryReducer.categories,
}
}
function mapDispatchToProps(dispatch) {
return {
homeAct: () => dispatch(homeAction()),
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(HomeScreen)
Is there any way to change tabBarBadge prop without re-rendering other components?
finnally after lots of banging my head to the wall, found the answer:
moved icon and title to a new component:
export const CartTabnavigator = (props) => {
const dispatch = useDispatch()
const color = props.color
useEffect(() => {
console.log("props.cart", props.cart)
dispatch(getCartAction())
}, [])
return (
<View>
<MaterialCommunityIcons name="cart-outline" color={color} size={26} style={{
transform: [
{ scaleX: -1 }
]
}} />
<View style={{ position: 'absolute', right: -5, top: -5, backgroundColor: Colors.red20, borderRadius: 9, width: 15, height: 15, justifyContent: 'center', alignItems: 'center' }}>
<Text white style={{ fontSize: 10, fontFamily: config.font }} >{props.cart.length}</Text>
</View>
</View>
);
}
const mapStateToProps = state => ({
cart: state.cartReducer.cart,
})
function mapDispatchToProps() {
return {}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(CartTabnavigator)
and used it in navigator like:
<Tab.Screen
name="Cart"
component={this.CartStack}
options={({ route }) =>
({
tabBarLabel: <Text default> سبد خرید </Text>,
tabBarIcon: ({ color }) => (
<CartTabNavigator color={color} />
),
})
}
/>
I am using Drawer,Stack and Tab navigator using React Navigation 5, I have followed documentation but Tab Navigator not showing.
Here's my code:
In my app.js I have called my main StackNavigator:
const Stack = createStackNavigator();
export default class App extends React.Component {
render() {
return (
<NavigationContainer>
<Stack.Navigator headerMode="none" initialRouteName="HomePage">
<Stack.Screen name="Home" component={HomePage} />
<Stack.Screen name="Login" component={LoginSignUp} />
<Stack.Screen name="DrawerScreenss" component={DrawerScreens} />
<Stack.Screen name="Tab" component={TabComp} />
</Stack.Navigator>
</NavigationContainer>
);
}
}
This is my Drawer Navigation Code:
import * as React from 'react';
import { createDrawerNavigator } from '#react-navigation/drawer';
import DrawerContent from './DrawerComponents/DrawerContent';
import DrawerHome from './DrawerComponents/DrawerHome';
const Drawer = createDrawerNavigator();
export default class DrawerScreens extends React.Component {
render() {
return (
<Drawer.Navigator
drawerContent={() => (
<DrawerContent navigation={this.props.navigation} />
)}>
<Drawer.Screen name="DrawHome" component={DrawerHome} />
</Drawer.Navigator>
);
}
}
Here's my TabNavigator which is not working:
import React, { Component } from "react";
import { createBottomTabNavigator } from "#react-navigation/bottom-tabs";
import DrawerHome from "./DrawerHome";
import Bookmarks from "./Bookmarks";
export default class TabComp extends Component {
render() {
const Tab = createBottomTabNavigator();
return (
<Tab.Navigator
initialRouteName="Home"
tabBarOptions={{
activeTintColor: "#e91e63",
activeTintColor: "red",
inactiveTintColor: "grey",
style: {
backgroundColor: "white",
borderTopColor: "red"
},
labelStyle: {
fontSize: 12,
fontWeight: "normal"
},
indicatorStyle: {
borderBottomColor: "red",
borderBottomWidth: 4
}
}}
>
<Tab.Screen name="Home" component={DrawerHome} />
<Tab.Screen name="Bookmarks" component={Bookmarks} />
</Tab.Navigator>
);
}
}
It should be visible in my DrawerHome screens but not working
I don't know exactly what your HomePage component looks like, but it could look something like this with a button that navigates to your DrawerScreens screen:
const HomePage = ({navigation}) => {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Feed Screen</Text>
<Button
title="Go to DrawerScreens"
onPress={() => navigation.navigate('DrawerScreens')}
/>
</View>
);
};
The important thing I changed in your DrawerScreens component is that its screen is your TabComp component:
class DrawerScreens extends React.Component {
render() {
return (
<Drawer.Navigator
drawerContent={() => (
<DrawerContent navigation={this.props.navigation} />
)}>
<Drawer.Screen name="Tab" component={TabComp} />
</Drawer.Navigator>
);
}
}
The TabComp component itself can stay the same.
Then inside your DrawerHome component you can trigger your drawer using navigation.openDrawer().
const DrawerHome = ({navigation}) => {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Feed Screen</Text>
<Button title="Open Drawer" onPress={() => navigation.openDrawer()} />
</View>
);
};
This way you can show your tabs and your drawer at the same time.
I'm working on an app with a Side Menu and a Navigator. In the side menu there are menu-items which let's you navigate (using Navigator), and the menu-items also get styled to indicate which one is active.
When first going to a new route with navigator.push() and then going back with navigator.pop(), the menu-item corresponding to the previously active route (which is now inactive) does not get either of the styles to show that it is either inactive or active. The style of the menu item (RouteMenuItem in the full example below) is as follows
style={[
{ padding: 15, borderColor: 'firebrick', borderWidth: 1 },
(isActive
? {backgroundColor: 'green'}
: {opacity: 0.5}
)
]}
How is it possible that neither {backgroundColor: 'green'} nor {opacity: 0.5} gets applied?
The image below shows how the bug looks on Android: "Route ONE" is selected, however, the menu item for "Route TWO" does not have opacity: 0.5 as it should (it should be half transparent like the 3rd menu item).
Below is the full code for a minimal example to reproduce the bug. The component hierarchy is like this:
<Navigator renderScene={() => <SideMenu><View /></SideMenu>} />
PS: We use StyleSheet.create() in our code, but in the example below I've just defined the styles inline. It does not seem to make any difference.
import React from 'react';
import {View, Text, TouchableHighlight, Navigator, Dimensions} from 'react-native';
import SideMenu from 'react-native-side-menu';
/***************
/** Routes **/
/****************/
const ROUTES = {
ONE: () => ({ title: 'Route ONE' }),
TWO: () => ({ title: 'Route TWO' }),
THREE: () => ({ title: 'Route THREE' }),
};
/**************/
/** Main **/
/**************/
export default class Main extends React.Component {
render() {
return (
<Navigator
style={{flex: 1}}
initialRouteStack={[
ROUTES.ONE(),
]}
renderScene={(route, navigator) =>
<Scene route={route} navigator={navigator} />
}
/>
);
}
}
/***************/
/** Scene **/
/***************/
class Scene extends React.Component {
state = {
menuIsOpen: false,
}
openMenu = () => {
this.setState({ menuIsOpen: true });
}
onMenuOpenChanged = (menuIsOpen) => {
if (this.state.menuIsOpen !== menuIsOpen) {
this.setState({ menuIsOpen });
}
}
render() {
const {route, navigator} = this.props;
return (
<View style={{flex: 1}}>
<SideMenu
menu={
<Menu
route={route}
navigator={navigator}
/>
}
menuPosition="right"
bounceBackOnOverdraw={false}
onChange={this.onMenuOpenChanged}
isOpen={this.state.menuIsOpen}
openMenuOffset={Dimensions.get('window').width * 0.75}
disableGestures={false}
>
<Screen route={route} navigator={navigator} openMenu={this.openMenu} menuIsOpen={this.state.menuIsOpen} />
</SideMenu>
</View>
);
}
}
/**************/
/** Menu **/
/**************/
class Menu extends React.Component {
render() {
const {route, navigator} = this.props;
return (
<View style={{ flex: 1, backgroundColor: 'coral', paddingTop: 25 }}>
<Text>Currently at {route.title}</Text>
<RouteMenuItem forRoute={ROUTES.ONE()} route={route} navigator={navigator} />
<RouteMenuItem forRoute={ROUTES.TWO()} route={route} navigator={navigator} />
<RouteMenuItem forRoute={ROUTES.THREE()} route={route} navigator={navigator} />
</View>
);
}
}
const RouteMenuItem = ({forRoute, route, navigator}) => (
<TouchableHighlight onPress={() => navigator.push(forRoute)}>
<Text style={[ { padding: 15, borderColor: 'firebrick', borderWidth: 1 }, (forRoute.title === route.title ? {backgroundColor: 'green'} : {opacity: 0.5}) ]}>
Go to {forRoute.title} ({(forRoute.title === route.title ? 'current route' : 'NOT CURRENT ROUTE')})
</Text>
</TouchableHighlight>
);
/*****************************/
/** Screen, inside Menu **/
/*****************************/
class Screen extends React.Component {
render() {
const {route, navigator, openMenu, menuIsOpen} = this.props;
return (
<View style={{ flex: 1 }}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', backgroundColor: 'peachpuff', paddingTop: 25 }}>
<HeaderButton onPress={navigator.pop}>Back</HeaderButton>
<HeaderButton onPress={openMenu}>Menu</HeaderButton>
</View>
<View style={{ flex: 1, backgroundColor: 'white' }}>
<Text style={{ margin: 50, fontSize: 50 }}>
{route.title}
</Text>
</View>
</View>
);
}
}
const HeaderButton = ({onPress, children}) => (
<TouchableHighlight underlayColor="green" onPress={onPress}>
<Text style={{ padding: 10, borderColor: 'firebrick', borderWidth: 1 }}>
{children}
</Text>
</TouchableHighlight>
);
The problem occurs becauase children of TouchableHighlight gets default opacity (1) after the TouchableHighlight is pressed. As that is a more specific problem I've asked a new question here.
In this case the bug appears when going back to a previous route, because the menu item in that instance of the Menu was pressed (moving us to a new route before we could spot the bug).