React Navigation - Gradient color for Header - javascript

I am using React Navigation in React Native app and I want to change the backgroundColor for the header to be gradient and I found out there is a node module : react-native-linear-gradient to achieve gradient in react native.
I have Root StackNavigator like that :
const Router = StackNavigator({
Login: {
screen: Login,
navigationOptions: ({navigation}) => ({
headerTitle: <Text>SomeTitle</Text>
headerLeft: <SearchAndAgent />,
headerRight: <TouchableOpacity
onPress={() => { null }
</TouchableOpacity>,
headerStyle: { backgroundColor: '#005D97' },
}),
},
});
I can wrap Text or View to be gradient like that :
<LinearGradient colors={['#3076A7', '#19398A']}><Text style={styles.title}>{title}</Text></LinearGradient>,
How can I wrap the header background in the navigationOptions to use
the the LinearGradient module?
I know that I can create a custom header component and use it but when I am doing it all the native navigation animations from React Navigation not working like the Title Animation between two Routes so its not helping me.
Thanks for helping !

Just for your information, now with headerBackground props it's a way easier.
You can have a gradient header just doing this :
navigationOptions: {
headerBackground: (
<LinearGradient
colors={['#a13388', '#10356c']}
style={{ flex: 1 }}
start={{x: 0, y: 0}}
end={{x: 1, y: 0}}
/>
),
headerTitleStyle: { color: '#fff' },
}
This solution works good even with SafeArea for IosX

The solution of Mark P was right but now you need to define headerStyle and do the absolute positioning there:
navigationOptions: {
header: props => <GradientHeader {...props} />,
headerStyle: {
backgroundColor: 'transparent',
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
},
and the GradientHeader:
const GradientHeader = props => (
<View style={{ backgroundColor: '#eee' }}>
<LinearGradient
colors={['red', 'blue']}
style={[StyleSheet.absoluteFill, { height: Header.HEIGHT }]}
>
<Header {...props} />
</LinearGradient>
</View>
)

Similar to this issue: React Navigation; use image in header?
For a Linear Gradient you would simply do >
//imports
import { Image, StyleSheet, View } from 'react-native';
import { Header } from 'react-navigation' ;
import LinearGradient from 'react-native-linear-gradient';
//header
Create the Header component which is wrapped in the Linear Gradient.
by making the header backgroundColor: 'transparent' you will then show the Linear Gradient wrapping it.
const GradientHeader = props => (
<View style={{ backgroundColor: '#eee' }}>
<LinearGradient
colors={['#00a8c3', '#00373f']}
style={[StyleSheet.absoluteFill, styles.linearGradient]}
/>
<Header {...props} style={{ backgroundColor: 'transparent' }}/>
</View>
);
Return the screen with the header being your GradientHeader component.
const SimpleStack = StackNavigator({
Home: {
screen: MyHomeScreen,
},
}, {
navigationOptions: {
headerTitleStyle: { color: '#fff' },
header: (props) => <GradientHeader {...props} />,
}
});
Should look something like this with the above code.
Gradient Header

You can use LinearGradient component from the expo. It is a useful component and you can't install another library like react-native-linear-gradient. https://docs.expo.io/versions/latest/sdk/linear-gradient/. By the way, you can change the back button. It is easy.
You can implement it on inside screen with navigationOptions like that
static navigationOptions = ({ navigation }: any) => {
const onGoBack = () => navigation.goBack();
return {
header: (props: any) => <GradientHeader {...props} />,
headerStyle: { height: 68, backgroundColor: "transparent", color: colors.white },
headerTitle: "Sign Up",
headerTitleStyle: { color: colors.white, fontSize: 18 },
headerLeft: (
<TouchableOpacity style={{ width: 32, height: 32, paddingLeft: 8 }} onPress={onGoBack}>
<Image source={images.back} resizeMode="center" style={{ width: 32, height: 32 }} />
</TouchableOpacity>
),
};
};

Related

REACT NATIVE: Scroll View keep bouncing back up to the top doesn't scroll

I am trying to get my ScrollView to scroll down so I can see all my views but it keeps bouncing back up to the top.
What it looks like
The White panels below Physiological Classification are in the ScrollView and are supposed to scroll but it just bounces back to the top.
Here is where my ScrollView is
_renderContent = (section) => {
section.arrow = !(section.arrow);
return (
<ScrollView>
<QuestionPanels
Questions={section.Questions}
pickerDefaultValues={section.pickerDefaultValues}
pickerItemNames={section.pickerItemNames}
/>
</ScrollView>
);
};
Question Panels is just a component that generates Views based on a json file. If relevant here is the code for that as well.
import React, { Component, useState, setState} from 'react';
import { ScrollView, View, Text } from 'react-native'
import styles from '../../Styles/GeneralStyles';
import Picker from '../Picker/Picker'
import appData from '../../DataSheet/appData.json';
const Panel = (props) => {
return (
<View style={styles.dropDownCardPanel}>
<View style={styles.QuestionPanel}>
<Text style={styles.QuestionText}>{props.Question}</Text>
</View>
<View style={styles.AnswerPanel}>
<Picker
defaultVal= {props.pickerDefaultValues}
showButton={false}
pickerItemNames={props.pickerItemNames}
/>
</View>
</View>
);
}
export default class QuestionairePanels extends Component {
constructor(props){
super(props)
}
render() {
var panelList = [];
for(let i = 0; i < this.props.Questions.length; i++){
panelList.push(
<Panel
key={i}
Question={this.props.Questions[i]}
pickerDefaultValues ={this.props.pickerDefaultValues[i]}
pickerItemNames = {this.props.pickerItemNames[i]}
/>
);
}
return(
<View>
{panelList}
</View>
);
}
}
Here are the styles that matter for this
dropDownCardPanel: {
height: 200,
flexDirection: "row",
backgroundColor: '#faf2f2',
paddingTop: 5,
paddingBottom: 5,
borderBottomWidth: 1,
borderColor: 'black',
},
dropDownCardPanelText: {
fontSize: 15,
color: "black",
marginLeft: 10
},
icon: {
left: 20,
},
QuestionPanel: {
height: "100%",
width: "60%",
borderRightWidth: 1,
borderColor: 'black',
left: 3,
marginRight: 5,
alignItems: "center",
justifyContent:'center'
},
QuestionText: {
fontSize: 20,
},
AnswerPanel: {
height: "100%",
width: "40%",
right: 3
}
The reason why I was not able to scroll was that the tag needs a specific height for it to render for a scroll. So to fix this I had to put a view around the and set it to a specific height.
Corrected Code
section.arrow = !(section.arrow);
var scrollHeight = (section.Questions.length > 2) ? 600 : 400;
var headerDisplay = (section.name.match("Physiologic")) ? section.name + " Variables" : "Select Dominant Final Diagnosis";
return (
<View style={{height: scrollHeight}}>
<ScrollView>
<View style={styles.dropDownCardPanelHeader}>
<Text style={styles.dropDownCardPanelHeaderText}>
{headerDisplay}
</Text>
</View>
<QuestionPanels
Questions={section.Questions}
pickerItemNames={section.pickerItemNames}
/>
</ScrollView>
</View>
Later I will try to use Flex to set the height so the react native project can scale to all screens but so far it works for iOS

How To navigate from createMaterialTopTabNavigatorto other screen - React Navigation?

I have some issue when navigating from top Tabnavigator to other screens
so my app navigation is
My Orders Screen from Drawer => Top TabNavigatore (Accepted/Completed) => Order Details
In Route.js
I put every single navigation I want like Drawer - Auth navigation and so on, and I put a StackNavigator contain the Orders Screen like this:
const OrdersStack = createStackNavigator({
Orders: {
screen: Orders,
navigationOptions: ({ navigation }) => ({
headerLeft: (
// <TouchableOpacity onPress={() => navigation.dispatch(DrawerActions.toggleDrawer())}>
<TouchableOpacity
onPress={() => navigation.dispatch(DrawerActions.toggleDrawer())}
>
<Icon
name="ios-menu"
size={40}
style={{ margin: 10 }}
color="#2F98AE"
/>
</TouchableOpacity>
),
headerRight: <View />,
title: "My Orders",
headerTintColor: "#2F98AE",
headerStyle: {
borderBottomColor: "white"
},
headerTitleStyle: {
color: "#2F98AE",
// textAlign: "center",
flex: 1,
elevation: 0,
fontSize: 25
// justifyContent: "center"
}
})
}
});
In the Orders.js I put these:
import React, { Component } from "react";
import { createAppContainer, createStackNavigator } from "react-navigation";
import NavTabs from "../screens/NavTabs";
import NavOrderDetails from "../screens/NavOrderDetails";
// create a component
export default class Orders extends Component {
render() {
return <MyOrdersScreen />;
}
}
export const root = createStackNavigator({
NavTabs: NavTabs,
NavOrderDetails: NavOrderDetails
});
const MyOrdersScreen = createAppContainer(root);
As I mentioned in Orders.js it Contains Tabs and Order Details
In Tabs, I'm creating a createMaterialTopTabNavigator
import { createMaterialTopTabNavigator } from "react-navigation";
import AcceptedOrders from "../screens/AcceptedOrders";
import CompletedOrders from "../screens/CompletedOrders";
const MainTabs = createMaterialTopTabNavigator(
{
Accepted: { screen: AcceptedOrders },
Completed: { screen: CompletedOrders }
},
{
tabBarOptions: {
activeTintColor: "#fff",
inactiveTintColor: "#ddd",
tabStyle: {
justifyContent: "center"
},
indicatorStyle: {
backgroundColor: "#fcc11e"
},
style: {
backgroundColor: "#2F98AE"
}
}
}
);
export default MainTabs;
and another screen is OrderDeatils.js
import { createStackNavigator } from "react-navigation";
import OrderDetails from "../screens/OrderDetails";
import React, { Component } from "react";
import { View } from "react-native";
const OrderDetailsStack = createStackNavigator({
OrderDetails: {
screen: OrderDetails,
navigationOptions: () => ({
title: "Order Details",
headerRight: <View />,
headerTintColor: "#2F98AE",
headerStyle: {
borderBottomColor: "white"
},
headerTitleStyle: {
color: "#2F98AE",
flex: 1,
elevation: 0,
fontSize: 25
}
})
}
});
export default OrderDetailsStack;
Here are a screenShots it should explain what I mean
1- My Orders
2- Order Details
If i understand, you are concerned about the blank header that appears on top of the screen under your first header.
That one is created by createStackNavigator.
A the first Stack that creates the first Header named OrdersStack.
Inside that you have the root constant (probably, as there isn't the full code) that is creating the second header.
Inside root you are then defining your createMaterialTopTabNavigator with your two screens, that's showing the topBar with the label "accepted" and "completed".
To hide that white space you have to disable your root header doing:
export const root = createStackNavigator({
NavTabs: NavTabs,
NavOrderDetails: NavOrderDetails
},
{
defaultNavigationOptions:{
header:null
}
});
UPDATE.
You have two ways to fix this and still have a backButton:
1) You can either create a parent CustomHeader that, using react-navigation's withNavigation HOC, is aware about his childrens navigation state.
2) Dinamically hide the parent header when the second one is showing. You can accomplish this using this.props.navigation.dangerouslyGetParent().dangerouslyGetParent().setParams({showHeader:false})
then your OrdersStack would be:
const OrdersStack = createStackNavigator({
Orders: {
screen: Orders,
navigationOptions: ({ navigation }) => {
var defaultHeader={
headerLeft: (
<TouchableOpacity
onPress={() => navigation.dispatch(DrawerActions.toggleDrawer())}
>
<Icon
name="ios-menu"
size={40}
style={{ margin: 10 }}
color="#2F98AE"
/>
</TouchableOpacity>
),
headerRight: <View />,
title: "My Orders",
headerTintColor: "#2F98AE",
headerStyle: {
borderBottomColor: "white"
},
headerTitleStyle: {
color: "#2F98AE",
// textAlign: "center",
flex: 1,
elevation: 0,
fontSize: 25
// justifyContent: "center"
}
}
if (navigation.state.params)
return(navigation.state.params.showHeader?{defaultHeader}:null)
else return defaultHeader
}
}
});

How to set Background Image in React-navigation header [duplicate]

I'm using react navigation in a react native project and I want to customize the header with an image.
For a color I can use simple styling, but since react native doesn't support background images I need a different solution.
Update:
Since v2 of the library there's an special option for setting the header background, namely headerBackground.
This option accepts a React component, so when set to an Image component, it will use that.
For example:
export default createStackNavigator({
Home: {
screen: HomeScreen
},
}, {
navigationOptions: {
headerBackground: () => (
<Image
style={StyleSheet.absoluteFill}
source={{ uri: 'https://upload.wikimedia.org/wikipedia/commons/3/36/Hopetoun_falls.jpg' }}
/>
),
}
});
Working example: https://snack.expo.io/#koen/react-navigation-header-background
Old answer, for when still using React Navigation v1:
Creating a custom header with an image is actually really simple.
By wrapping the Header with a view and placing an absolute positioned image in that view, the image will scale to its parent size.
Important is to set the backgroundColor of the default header to transparent.
const ImageHeader = props => (
<View style={{ backgroundColor: '#eee' }}>
<Image
style={StyleSheet.absoluteFill}
source={{ uri: 'https://upload.wikimedia.org/wikipedia/commons/3/36/Hopetoun_falls.jpg' }}
/>
<Header {...props} style={{ backgroundColor: 'transparent' }}/>
</View>
);
And then use that component as header:
const SimpleStack = StackNavigator({
Home: {
screen: MyHomeScreen,
},
}, {
navigationOptions: {
headerTitleStyle: { color: '#fff' },
header: (props) => <ImageHeader {...props} />,
}
});
Which would result in:
According to the official docs of react-navigation v5, it can be implemented as follows:
https://reactnavigation.org/docs/headers/#replacing-the-title-with-a-custom-component
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
// title: 'App Name'
options={{
headerTitle: (props) => ( // App Logo
<Image
style={{ width: 200, height: 50 }}
source={require('../assets/images/app-logo-1.png')}
resizeMode='contain'
/>
),
headerTitleStyle: { flex: 1, textAlign: 'center' },
}}
/>
</Stack.Navigator>
Update for React Navigation v5! (making this post for future references)
For react navigation 5, I found this solution.
In StackNavigator.js class you can set a different image for each page (Stack.Screen):
<Stack.Screen
name='Home'
component={HomeScreen}
options={{
title: <Image style={{ width: 250, height: 50 }}
source = require('../images/yourimage.png')}/>
}}
/>
Then, you must adjust width, height, and position of the image, but it works! I think it's the simpliest way. Here's the output (yes, it's my image, before adjustments).
Don't forget to import Image!
import { Image } from 'react-native'

How to render the same component on all screens?

I have a StackNavigator, where I've specified the same headerRight Icon for every screen:
export default StackNavigator(
{
Authorization: {
screen: AuthorizationScreen
},
SignIn: {
screen: SignInScreen
},
SignUp: {
screen: SignUpScreen
},
Main: {
screen: MainScreen
},
Language: {
screen: LanguageScreen
},
//...etc
},
{
navigationOptions: {
headerRight: (
<Icon color={'#77767c'}
name='ios-contact-outline'
size={30}
style={{ paddingRight: 30}}
type='ionicon'
/>
),
}
}
)
All the screens are imported from separate files. When this Icon is pressed, I want the same component to render regardless of what screen I'm on. The problem is, I can't think of a way to do this outside of writing in some kind of state handling and onPress function for every single screen I have, which would be really tedious to write and maintain. Is there any way to get around this and only write the component rendering once?
You can create one component for Header and pass it into all the screen's navigationOptions. Then you just need to handle the method on each screen and you can do your stuff at here.
Custom Header Class:
class Header extends Component {
render() {
const props = this.props;
return (<View>
<View
style={KEEP_YOUR_STYLE}
/>
<View style={styles.containerStyle} >
<Text
style={your_style}
numberOfLines={1}
>TITLE</Text>
<TouchableOpacity
style={styles.touchableOpacityStyle}
onPress={props.onPress}
>
<Image
source={YOUR_ICON}
style={{
position:'absolute',
right: 10,
width: 20,
height: 20,
resizeMode: 'cover',
}
} />}
</TouchableOpacity>
</View>
</View>
);
}
}
export { Header };
In your StackNavigator:
const defaultNavigation = ({ navigation }) => ({
header: (<Header
title='Hellow'
/>),
});
Language: {
screen: LanguageScreen,
navigationOptions: defaultNavigation,
},
In your particular Screen:
static navigationOptions = ({ navigation }) => ({
header: (
<Header
title='Your Title'
onPress={() => {
// DO YOUR STUFF
}}
/>),
});

Styling reusable components in React-Native

I made a reusable component Button.js and I'm importing it on two different screens. The button looks the same, but on the first screen I need it to be position: 'absolute' and on the second one position: 'relative' (the default).
How do I add the position to be absolute on the first screen? I tried to add the styling on FirstPage.js but it does not work. How do I overwrite the style that is defined in Button.js?
Button.js:
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
const Button = ({ position, onPress, children }) => {
const { buttonStyle, textStyle } = styles;
return (
<TouchableOpacity onPress={onPress} style={buttonStyle, {'position': position}}>
<Text style={textStyle}>{children}</Text>
</TouchableOpacity>
);
};
Button.defaultProps = {
position: 'relative',
}
const styles = {
textStyle: {
alignSelf: 'center',
color: '#F44336',
fontSize: 16,
},
buttonStyle: {
zIndex: 2,
width: 100,
backgroundColor: '#FFF',
}
};
export { Button };
You can pass props, something like this (Button.js) (edited according to posted code):
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
const Button = ({ position, onPress, children }) => {
const { buttonStyle, textStyle } = styles;
const style = {...buttonStyle, position };
return (
<TouchableOpacity onPress={onPress} style={style}>
<Text style={textStyle}>{children}</Text>
</TouchableOpacity>
);
};
Button.defaultProps = {
position: 'relative',
}
const styles = {
textStyle: {
alignSelf: 'center',
color: '#F44336',
fontSize: 16,
},
buttonStyle: {
zIndex: 2,
width: 100,
backgroundColor: '#FFF',
}
};
export { Button };
Your button of course looks different, this is just an outline of what you could do (basically just using props).
This is REUSABLE Button as touchableOpacity.
export const NormalThemeButton = (props) => {
return (
<TouchableOpacity
style={[{ ...props.style, ...styles.normalThemeBtn }]}
style={[{ alignItems: props.align }, styles.anchor]}
onPress={props.onPress} >
<CustomText text={props.text} l bold style={{
textAlign: 'center',
color: theme['blackColor']
}} />
{props.children}
</TouchableOpacity >
);
};
This is where this RESUABLE Button is been used.
style={{ ...styles.openArrivedButton }}
text={Lng.instance.translate('useWaze')}
onPress={() => { Waze() }}/>
Hope You find it helpful.

Categories