React Navigation - wrapping header and tab navigator in Blurview looses props - android

I am using React Navigation 2 for a simple RN project with Expo. I am trying to get the header and tabs on the bottom to display over a blurred background so I have done a HOC to wrap the library Header with a BlurView to provide that functionality. It renders the blur fine but unfortunately the title, back buttons etc. are lost in the process. Is there a way to do that in React Navigation, the code I use is as follows:
const wrappedHeader = props => (
<BlurView tint="light" intensity={80} style={styles.header}>
<Header {...props}/>
</BlurView>
);
class HomeScreen extends React.Component {
static navigationOptions = {
header: props => wrappedHeader(props),
headerTitle: "Home Screen",
};
....
}

This is a tricky question that truly got me thinking for awhile.
Here's the solution I've found to get a native iOS feeling for a tab bar navigator:
import React from 'react';
import { StyleSheet } from 'react-native';
import { BlurView } from 'expo';
import { BottomTabBar } from 'react-navigation-tabs';
const styles = StyleSheet.create({
blurView: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
},
bottomTabBar: {
backgroundColor: 'transparent',
},
});
export default function TabBar(props) {
return (
<BlurView tint="light" intensity={90} style={styles.blurView}>
<BottomTabBar {...props} style={styles.bottomTabBar} />
</BlurView>
);
}
The problem seemed to be related to the BlurView styling.
Note: this code will only work after setting the tabBarComponent option on your navigator as the following:
export default createBottomTabNavigator({
// This part should be different on your side
Feed: FeedScreen,
Me: MeScreen,
Settings: SettingsScreen,
}, {
tabBarComponent: TabBar,
});
For the header, I guess it must be the same trick, but you would need to replace bottom: 0 with top: 0.

Related

Change barStyle React Native based on the rendered background image

I can't seem to find this anywhere or I might be using the wrong keyword to search, but how do I change the barStyle on React Native (light-content/dark-content) based on the rendered background image's color
edit:
I have single screen which renders different kinds of images, what I want to achieve is how to change the barStyle based on what image currently showing on the screen. Is there even any way to do that?
case 1:
light-background image with dark-content barStyle
case 2
dark-background image with light-content barStyle
import statusbar and add in render method and set barStyle according to what you want.
try this
import { StatusBar, View } from "react-native";
render() {
return (<View>
<StatusBar barStyle="light-content" />
.....
</view>);
}
Setting StatusBar as transluent along with backgroundColor as transparent, you can achieve this which is as follows:
import React, { Component } from "react";
import { ImageBackground, StyleSheet, StatusBar } from "react-native";
const image1 = { uri: "https://htmlcolorcodes.com/assets/images/html-color-codes-color-tutorials-hero.jpg" };
const image2 = { uri: "https://reactjs.org/logo-og.png" };
export default class App extends Component {
componentDidMount() {
StatusBar.setTranslucent(true);
StatusBar.setBackgroundColor("transparent");
}
render() {
return (
<ImageBackground source={image1} style={styles.image}>
</ImageBackground>
);
}
}
const styles = StyleSheet.create({
image: {
flex: 1,
resizeMode: "cover"
}
});
If you use image1, your output will be like this picture.
If you use image2, your output will be like this picture.

How to navigate to another page in react native ignite bowser?

I am new to react native / ignite bowser. I am making a react native / ignite bowser project.
In my app first there is a welcome screen which appears when the app starts. There is a 'continue' button in the welcome screen. When I click the 'continue' button it should go to the login screen. But it shows this error:
TypeError: undefined is not an object (evaluating 'navigation.navigate')
I am running the app on an actual physical android device.
Can anyone help me? This is my code in github:
https://github.com/boidurja/smartcope_new.git
This is the part of the code where the error occurs:
<Button title="Countinue" containerStyle={{ flex: 1 }} onPress={ ( ) => navigation.navigate('login') }/>
in this file:
app/screens/auth-screens/welcome-screen.tsx
I was told that in this case the issue is that the props property doesn't have the navigation object, coz in new version of the library we have to use react hooks for that check for react-navigation v5 documentation on how to access the navigation prop in react component. I tried it but was not successful.
I see this error
You don't seem to receive the Navigation object properly. If this is the component you use, use the useNavigation function.
useNavigation is a hook which gives access to navigation object.
It's useful when you cannot pass the navigation prop into the
component directly, or don't want to pass it in case of a deeply
nested child.
import { useNavigation } from '#react-navigation/native';
export const AuthScreensWelcomeScreen: FunctionComponent<AuthScreensWelcomeScreenProps> = observer((props) => {
...
const navigation = useNavigation();
...
<Button title="Countinue" containerStyle={{ flex: 1 }} onPress={ ( ) => navigation.navigate('login') }/>
OR
You can get access to the root navigation object through a ref and
pass it to the RootNavigation which we will later use to navigate.
Usage
import { NavigationContainer } from '#react-navigation/native';
import { navigationRef } from './RootNavigation';
export default function App() {
return (
<NavigationContainer ref={navigationRef}>{/* ... */}</NavigationContainer>
);
}
// RootNavigation.js
import * as React from 'react';
export const navigationRef = React.createRef();
export function navigate(name, params) {
navigationRef.current?.navigate(name, params);
}
// add other navigation functions that you need and export them
// any js module
import * as RootNavigation from './path/to/RootNavigation.js';
export const AuthScreensWelcomeScreen: FunctionComponent<AuthScreensWelcomeScreenProps> = observer((props) => {
// ...
<Button title="Countinue" containerStyle={{ flex: 1 }} onPress={ ( ) => RootNavigation.navigate('login') }/>
In welcomscreen.tsx, try changing this line:
const {
navigation
} = props;
To this:
const {
navigation
} = this.props;
Alternately, you could leave that snippet out and alter the button snippet like this:
<Button title="Countinue" containerStyle={{ flex: 1 }} onPress={() => this.props.navigation.navigate('login') }
Hope that helps.

How to use SafeAreaView for Android notch devices?

I'm developing an app with React Native and I'm testing with my OnePlus 6 and it has a notch. The SafeAreaView is a solution for the iPhone X but for Android, it seems there is no solution.
How to solve this kind of issue?
Do something like
import { StyleSheet, Platform, StatusBar } from "react-native";
export default StyleSheet.create({
AndroidSafeArea: {
flex: 1,
backgroundColor: "white",
paddingTop: Platform.OS === "android" ? StatusBar.currentHeight : 0
}
});
And then In your App.js
import SafeViewAndroid from "./components/SafeViewAndroid";
<SafeAreaView style={SafeViewAndroid.AndroidSafeArea}>
<Layout screenProps={{ navigation: this.props.navigation }} /> //OR whatever you want to render
</SafeAreaView>
This should work good as get height will take care of the knotch in android device by calculating the statusBar height and it will arrange accordingly.
A work around I had to use recently:
GlobalStyles.js:
import { StyleSheet, Platform } from 'react-native';
export default StyleSheet.create({
droidSafeArea: {
flex: 1,
backgroundColor: npLBlue,
paddingTop: Platform.OS === 'android' ? 25 : 0
},
});
It is applied like so:
App.js
import GlobalStyles from './GlobalStyles';
import { SafeAreaView } from "react-native";
render() {
return (
<SafeAreaView style={GlobalStyles.droidSafeArea}>
//More controls and such
</SafeAreaView>
);
}
}
You'll probably need to adjust it a bit to fit whatever screen you're working on, but this got my header just below the icon strip at the top.
Late 2020 answer: For anyone stumbling across this issue themselves, they have added support for this.
Follow this documentation page
You could also create helper component with this style applied right away like this
import React from 'react';
import { StyleSheet, Platform, StatusBar, SafeAreaView } from 'react-native';
export default props => (
<SafeAreaView style={styles.AndroidSafeArea} {...props} >
{props.children}
</SafeAreaView>
);
const styles = StyleSheet.create({
AndroidSafeArea: {
paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight : 0
}
});
Make note that I also deleted unnecessary styles which breaks natural behavior of SafeAreaView which in my case broke styling.
As for use you simply use it like normal SafeAreaView:
import React from 'react';
import SafeAreaView from "src/Components/SafeAreaView";
render() {
return (
<SafeAreaView>
// Rest of your app
</SafeAreaView>
);
}
}
for more consistency import:
import { Platform, StatusBar } from "react-native";
and then use it like so:
paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight : 0
if you're seeing this in 2020 and you also need the web support with the Android and iOS, type this in your terminal.
expo install react-native-safe-area-context
this will install the updated safe area context.
Then import the following stuffs into your app.js
import { SafeAreaView, SafeAreaProvider} from "react-native-safe-area-context";
add <SafeAreaProvider> before all the tags in your main function in app.js, also remember to close it at the end.
and finally, instead of view, add SafeAreaView.
Read more at the official expo website : SafeAreaContext
Although the docs says it is relevant only for iOS, when I used React's SafeAreaView it acted differently on different screens on Android.
I managed to fix the problem by implementing my version of SafeAreaView:
import React from "react";
import { Platform, View, StatusBar } from "react-native";
import { GeneralStyle } from "../styles";
export function SaferAreaView({ children }) {
if (Platform.OS == "ios") {
return <SaferAreaView style={{ flex: 1 }}>{children}</SaferAreaView>;
}
if (Platform.OS == "android") {
return <View style={{flex: 1, paddingTop: StatusBar.currentHeight}}>{children}</View>;
}
}
This was tested on an old device (with hardware navigation) and new notch devices (with software navigation) - different screen sizes.
This is currently the best or easiest way to implement SafeAreaView on Android and ios for both vanilla RN and Expo.
import { SafeAreaView } from 'react-native-safe-area-context';
function SomeComponent() {
return (
<SafeAreaView>
<View />
</SafeAreaView>
);
}
1 - expo install expo-constants
2- and do like this for example
import React from "react";
import Constants from "expo-constants";
import { Text, StyleSheet, SafeAreaView, View } from "react-native";
export default function HeaderTabs({ style }) {
return (
<SafeAreaView style={[styles.screen, style]}>
<View style={[styles.view, style]}>
<Text>Hello this is status bar</Text>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
screen: {
paddingTop: Constants.statusBarHeight,
flex: 1,
},
view: {
flex: 1,
},
});
Instead of using Platform API, you can use expo constants.
npm i expo-constants
then import it in your component as
import Constants from "expo-constants"
and then in the styles you can use it like this
const styles = StyleSheet.create({
container: {
paddingTop: Constants.statusBarHeight
} });
To see all the properties of Constants console log it you will find some more useful things.
Well, I had the same problem. I solved this using this lib React Native Status Bar Height, and I recommend because it´s a piece of cake to use.
And if you are using style-components you can add the getStatusBarHeight() on your styles.js like I did on the example below:
import styled from 'styled-components/native';
import { getStatusBarHeight} from 'react-native-status-bar-height';
export const Background = styled.View`
flex:1;
background:#131313;
margin-top: ${getStatusBarHeight()};
`
In the SafeAreaView Docs was told:
It is currently only applicable to iOS devices with iOS version 11 or later.
So now I definitely use it in my project but I use Platform to recognize device platform and for Android, I make a manual safe area for the status bar.
you can use react-native-device-info for device info and apply styling also with a notch
I used StatusBar from react-native instead of expo-status-bar and this worked for me on my OnePlus as well as other Android devices.
import { StatusBar } from 'react-native';
Expo solution(docs - android only):
import { setStatusBarTranslucent } from 'expo-status-bar';
Then in the component you can use useEffect hook:
useEffect(() => {
setStatusBarTranslucent(false)
},[])
for iOS you can use the <SafeAreaView> component from react-native.
ENRICO SECCO was right (i cant comment due to my stackoverflow reputation lol)! any safeareaview thingy doesn't work for me as well, so i get around with
import { getStatusBarHeight} from 'react-native-status-bar-height';
here how execute it, keep in mind that this is in my app.js, where i put all my stack.navigator + bottomtab.navigator
export default function App() {
//IGNORE ALL OF THIS, JUMP TO THE RETURN() FUNCTION!
const [appIsReady, setAppIsReady] = useState(false);
useEffect(() => {
async function prepare() {
try {
await SplashScreen.preventAutoHideAsync();
await Font.loadAsync(AntDesign.font);
await Font.loadAsync({
'Montserrat-Bold': require('./assets/fonts/Montserrat-Bold.ttf'),
'Montserrat-Regular': require('./assets/fonts/Montserrat-Regular.ttf'),
'Montserrat-Light': require('./assets/fonts/Montserrat-Light.ttf'),
});
await new Promise(resolve => setTimeout(resolve, 2000));
} catch (e) {
console.warn(e);
} finally {
// Tell the application to render
setAppIsReady(true);
}
}
prepare();
}, []);
const onLayoutRootView = useCallback(async () => {
if (appIsReady) {
await SplashScreen.hideAsync();
}
}, [appIsReady]);
if (!appIsReady) {
return null;
}
return (
//HERE!
<NavigationContainer>
<View style = {{
flex: 1, <- TO MAKE IT FULL SCREEN (PLEASE DELETE THIS)
marginTop: getStatusBarHeight(), <- TO PUSH IT DOWN FROM OFF SCREEN, MINE RAN OFF TO THE TOP LMAO (PLEASE DELETE THIS)
}} onLayout={onLayoutRootView}>
<Tabs/>
</View>
</NavigationContainer>
);
}

Possible Android autocomplete bug on twice backspace (changing value to include previous word)

This is possibly just an Android 6.0 bug. I tested the snack below in Android 5.1.1 and Android 7.0 and it didn't happen there.
I am trying to do an autocomplete whenever the user types "#". I successfully do this, however once I backspace a couple times, the value on the native side becomes some value I never had before. I have simplified the case to this code below:
Please try the snack here - https://snack.expo.io/#noitsnack/what-the-heck---autocomplete-then-backspace-bug OR copy and paste the code into a new react-native init project. I tested in RN 0.51 and RN 0.54.
Please then type Hi #
You will see it autocompletes to Hi #foobar.
Then backspace once and it properly is now Hi #fooba.
Backspace again, and now it is Hi #foHi (this is the bug, it should be Hi #foob)
This is a controlled input. I have no idea why it's turning into Hi #foHi on second backspace. If I blur then come after step 3 it doesn't come back.
I tried on two other devices, Android 7.0 and Android 5.1.1, and this bug was not there. It only happened on my Android 6.0. I think this is a OS dependent bug. Does anyone have ideas on what is actually going on? That will help me on how to work around this on all devices.
Is this really a bug on RN side?
I recorded a screencast of this behavior here in HD:
https://gfycat.com/RectangularAltruisticEuropeanfiresalamander
Here is a GIF:
The code (copied from the expo snack):
import React, { Component } from 'react';
import { StyleSheet, Text, View, TextInput } from 'react-native'
class FieldMentionable extends Component<Props, State> {
state = {
value: ''
}
render() {
const { value } = this.state;
return <TextInput onChange={this.handleChange} value={value} multiline />
}
handleChange = ({ nativeEvent:{ text }}) => {
const { value } = this.state;
if (text.endsWith(' #')) this.setState(() => ({ value:text + 'foobar' }));
else this.setState(() => ({ value:text }));
}
handleRef = el => this.input = el;
}
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<FieldMentionable />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 100,
backgroundColor: '#F5FCFF',
}
});
This is a bug on and its root issue has been filed here - https://github.com/facebook/react-native/issues/19085

Different Headers in StackNavigator

I have implemented a StackNavigator in which I removed the header for styles purpose.
Now I have a Component inside the StackNavigator for which I'd like to get the header back. How do you reckon I do it ?
Here is my StackNavigator :
const Nav = StackNavigator(
{
SplashScreen: {screen: SplashScreen},
Login: {screen: Login},
Register: {screen: Register},
Main: {screen: MainNav},
},
{
headerMode: 'none',
});
But for the Register screen, I would like to have a header (to let the user know that he can go back).
I tried doing this :
static navigationOptions = {
title: 'Register',
header: {
}
}
But I don't quite know what to put inside the header part.
Personally what i did: I had set the height of the header to 0 when i didnt want it to show and to n to show it so i could do somenthing like
height: condition ? 0 : 10
otherwise someone answered here
You can do it programmatically with the following code:
static navigationOptions = ({ navigation }) => ({ header:
> (navigation.state.params.thanks) => <HeaderView
> content={navigation.state.params.thanks} /> : null, })
And then you
can set the state params with this:
componentWillMount() { this.props.navigation.setParams({ thanks:"Something" }); }
Although I haven't tried the code myself and I'm not
sure if something like the HeaderView is accessible for the public api
in react-navigation and if there is a content property on that. But I
think in an abstract way, this is how you set it programmatically.
I didn't try it either but it might work
Edit: the answer given in github is definitely better, you can import HeaderView from react-navigation and do header: condition ? HeaderView : null to show and hide it

Categories

Resources