Set Header Height with Safe Area Insets with React Navigation - react-native

I can pass screenOptions to the Navigator with
a headerStyle object with a height property, but I'd like the height to take into account the SafeAreaInsets and not be a fixed value
There's getDefaultHeaderHeight function that takes into account the statusBarHeight here https://github.com/react-navigation/react-navigation/blob/dbe961ba5bb243e8da4d889c3c7dd6ed1de287c4/packages/drawer/src/views/Header.tsx#L8 - is there a way I can call this function and simply add n pixels across all devices?

I'm not sure about in React Navigation 5, but height is not a supported property of headerStyle in React Navigation 6. The rest of this response is based on references to and my personal experience with React Navigation 6.
You may need to write a fully custom header component to achieve what you want. React Navigation even has a Guide on supporting safe areas. Summarizing the Guide from React Navigation, plus a little extra real-world usage of it, you can then use the useSafeAreaInsets hook from react-native-safe-area-context and then in the style for the View surrounding your custom header, you can use the top value from the hook response as paddingTop to ensure that your header avoids the status bar area. If the rest of the height of your header needs to be explicitly defined, you'll need to add that top value to your height number as the height property of this View. If the height of your header is dynamic and based on its content, you probably don't actually need to set an explicit height on your header.
Example:
import React, { useEffect } from 'react';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const MyComponent = () => {
const insets = useSafeAreaInsets();
const navigation = useNavigation();
useEffect(() => {
navigation.setOptions({
// only us height if you REALLY need it, height should be dynamic based on content
header: () => <View style={{ paddingTop: insets.Top, height: value + insets.Top }}>
{content of your custom header}
</View>
});
}, [insets, navigation]);
};

Related

How to make a React Native TextInput change Opacity just like TouchableOpacity?

My code looks something like this currently:
<View>
<TextInput placeholder='PlaceholderText'>
</TextInput>
</View>
I want to make a TextInput component that has an opacity animation on click (exactly like TouchableOpacity changes opacity on click).
I tried wrapping the TextInput inside TouchableOpacity, but it doesn't work since the touchable component surrounds text input. Is there a standard React Native or StyleSheet way of doing this or would I have to manually create an animation to mimic that effect?
Simply wrap your TextInput in a View element and animate the View's opacity color from the onFocs event of the TextInput. TextInput doesn't have the opacity attribute, therefore the additional view is required for your goal.
Following code may give you an idea how to solve it. Have to admit, that I haven't tested the code, it may contain some error, so use it carefully.
// First create an animated value to use for the view's opacity.
const textInputAnimOpacity = useRef(new Animated.Value(1.0)).current;
// create a method to set the opacity back to 1.0
const showTxtInput = () => {
Animated.timing(textInputAnimOpacity, {
toValue: 1.0, // value to reach
duration: 250 // time in ms
}).start();
};
// this animated method differs from the first one by (obviously the value 0.7)
//and the callback part that goes into the start()-method
const blurTxtInput = () => {
Animated.timing(textInputAnimOpacity, {
toValue: 0.7, // value to reach
duration: 250 // time in ms
}).start(({ finished }) => {
showTxtInput(); // Callback after finish, setting opacity back to 1.0
});
};
/*Set the opacity to the value, we want to animate*/
<View style={{opacity:textInputAnimOpacity}}>
/* call blurTxtInput to set the value to 0.7 and again to 1.0 once it reaches 0.7*/
<TextInput onPressIn={blurTxtInput} placeholder='PlaceholderText'>
</TextInput>
</View>
If you just want to set opacity, make your styles change using the onPressIn and onPressOut props:
const [pressed, setPressed] = useState(false);
// in render
<TextInput
onPressIn={() => setPressed(true)}
onPressOut={() => setPressed(false)}
style={pressed ? styles.textInputPressed : styles.textInput}
// ...
/>
If you need the changes to animate, you can do that with the built-in RN Animated component or react-native-reanimated, using the same props to trigger the animations.

Adjust the scrollview height dynamically at runtime

I am developing a react-native project.
I have a ScrollView in MyComponent, the content of ScrollView consists of :
a MySubComponent,
a Text
a Image component
All the content/data of above components have dynamic length or height. So, I would like adjust the content height of my ScrollView on the fly at runtime.
To achieve that my plan is to disable automaticallyAdjustContentInsets of the ScrollView as you can see from my below code snippet. Then, have a state variable to hold the latest value of contentInsetBottom. But I am not sure how can I calculate the height of child components so that I can call setContentInsetBottom(totalHeight) to update the content height of my ScrollView .
(I am pretty sure my plan will work if I know how to calculate the height of each child component of my ScrollView.)
Anyone can guide me a bit?
const MyComponent = ({myText, image}) => {
// I have a state of the contentInsetBottom for the ScrollView
const [contentInsetBottom, setContentInsetBottom] = useState(0);
// how can I get the height of each children component, sum them up & call setContentInsetBottom(totalHeight) here ?
return (
<ScrollView
automaticallyAdjustContentInsets={false}
contentInset={{top: 0, bottom: contentInsetBottom}}
style={styles.scrollView}
contentContainerStyle={styles.contentContainer}>
<MySubComponent />
<Text>{myText}</Text>
<Image source={{uri: image?.uri}}>
</ScrollView>)
}
Wrap all content inside the <ScrollView> in a <View>. Then use onLayout to get the height of that parent View.
const handleScrollContentLayout = (e) => {
const { height } = e.nativeEvent.layout
setScrollLayoutHeight(height)
}
...
<View onLayout={handleScrollContentLayout}>
{ /* scrollView content... */ }
</View>
Then you can use the scrollLayoutHeight as per your needs to set the height at runtime.

React native navigation newbie: Fullscreen width on a showInAppNotification?

I'm using react-native-navigation to show error notification dropdowns via 'showInAppNotification'.
I've tried to style the dropdown box with:
{
backgroundColor: colorRed,
flex: 1,
alignSelf: 'stretch',
}
I can't get the box to obey that flex call. It stays as the width of the text inside the notification.
That is, what I get is this:
And what I want is this:
(The second is achieved by setting a hard width of 999, but that isn't a satisfactory solution)
So, from my limited understanding of React Native's stylesheet logic, I assumed I had a parent element with a fixed width above the notification. Except I don't. It's called directly (well, unless I'm missing some sort of injected showInAppNotification component) into my Provider HOC, which looks like this:
<Provider store={store}>
<ErrorBoundary>
{children}
</ErrorBoundary>
</Provider>
To confirm the ErrorBoundary was fullscreen width, I threw a backgroundColor: green on the error boundary. It came back as the expected width, like this:
Any thoughts on what could be going on? As mentioned I'm new to react native & it's possible I'm missing something obvious w/regards to flex logic, but I suspect it's a react-native-navigator notification issue I'm hoping others have run into. Any thoughts appreciated.
Instead of giving your width a hard-coded value you can import react native's Dimensions class into your component and use it in order to set the width.
So, your code can look something like this:
import { ...., Dimensions } from 'react-native';
const { width } = Dimensions.get('window');
const styles = {
notification : {
backgroundColor: colorRed,
width,
alignSelf: 'stretch'
}
}

React Native - Using "flex" inside a FlatList

I'm starting to learn React Native, and I'm trying to create grid of 3 columns with images. I've been using the numColumns prop of the FlatList to specify 3 columns, and then setting flex:1 for my images so they should fill the space of the column. However flex:1 makes none of my images appear, while trying height:100,aspectRatio:1 shows all of my images in columns. Any idea why this is? My code is down below:
export default class ArtScrollView extends React.Component {
_renderItem = (item) =>
(
<Image style={styles.art} source={{uri:item.item.imgFilePath}}/>
)
render() {
return(
<FlatList numColumns={3}
data={Object.values(this.props.pods)}
renderItem={this._renderItem}/>
);
}
}
const styles = StyleSheet.create({
art:{
height:100,
aspectRatio:1,
//flex:1, <- Having this instead of specifying the height doesn't work
marginRight:10,
}
});
Auto-sizing images in ReactNative does not work. It just doesn't. You need to know the dimensions of images before you show them.
It is quite easy to use var {height, width} = Dimensions.get('window'); and use those as your reference for your images' sizing.
Use the inspector and see (and understand) what FlatList actually renders for you. You may understand why things do not work (not just in this case but generally)

React Native how to do min/max widths

I want to style a component in my interface. The component must have a width of at least 200, but I want to let it grow with screen width to up to 600. But, sometimes people use tablets or huge phones. And I don't want the component to be able to grow with the screen forever. I want it to have a maximum width of 600.
And I know maxWidth is a thing that is, at least for now, not a part of the flexbox implementation in React Native... so, is there a reasonable way to do this today?
You can use the maxWidth, maxHeight, minWidth, minHeight layout properties supported by React Native.
Document here React Native layout props.
Example:
StyleSheet.create({
container: {
maxWidth: '80%', // <-- Max width is 80%
minHeight: 20, // <-- Min height is 20
},
});
There is no such thing as "maxWidth" in React Native. You may want to style your component at run-time. Try playing with Dimensions. You can get screen width and screen height of the device and adjust width of your component accordingly.
You can define two different style objects.
For full-width component on a device having width less than 600.
componentStyle_1: {
flex: 1
}
For 600 width on a device having width greater than 600
componentStyle_2: {
width: 600
}
You can check the device width runtime.
var {height, width} = Dimensions.get('window');
if(width>600){
//load componentStyle_1
}
else{
//load componentStyle_2
}
Best way to get accurate results is to play with your code. Good luck!
Refer: https://facebook.github.io/react-native/docs/dimensions.html#content
Simple. Just use maxWidth in your styles.
In a practical manner, this is how you would use it:
import { StyleSheet, Text, View, Dimensions, (+ anything else you need such as Platform to target specific device widths } from "react-native";
// plus whatever other imports you need for your project...
In a class component, you would create a state called whatever, let's say deviceWidth. Then inside the component you would use:
constructor(props) {
super(props);
this.state = {
deviceWidth: 375, // Put in any default size here or just "null"
// plus any other state keys you need in your project
}
componentDidMount() {
const currentWidth = Dimensions.get("screen").width;
this.setState({deviceWidth: currentWidth});
}
In a functional component you would import:
import React, { useEffect, useState } from "react";
Then inside your functional component you would add in:
const [currentWidth, setCurrentWidth] = useState(null //or add in a default width);
useEffect(() => {
const currentWidth = Dimensions.get("screen").width;
setCurrentWidth({deviceWidth: currentWidth});
}, []);
You could also use:
const deviceDisplay = Dimensions.get("window");
const deviceHeight = deviceDisplay.height;
const deviceWidth = deviceDisplay.width;
..if you wanted to find the height as well. On Android, "window" gets you the full screen height including the upper bar while "screen" gets you height without upper bar. Window and screen on iOS are the same.
Then using inline styles so that you have access to the state, set the width & maxWidth:
<View style={[styles.wrapper, { width: this.state.deviceWidth, maxWidth: 400, // or whatever you want here. } ]} >
Any width settings in a wrapper style found in your StyleSheet object will be over-ridden by the inline style, just like in CSS.
Alternatively if you don't have any other styles declared in your StyleSheet object, just use:
<View style={{ width: this.state.deviceWidth, maxWidth: 400 }} >
Or in a functional component, that would be:
<View style={{ width: deviceWidth, maxWidth: 400 }} >