SafeAreaView implementation on iPhone 11 - ios

I'm trying to add a SafeAreaView so my icons don't get squished together like shown in the image.
But it seems the SafeAreaView just pushes the contents up instead.
This is how I've currently Implemented it/
const BottomTabs = () => (
<SafeAreaView style={styles.container}>
<DevSupport />
<Tab.Navigator>
<Tab.Screen
name="Home"
component={FeaturedScreen}
options={{
unmountOnBlur: true,
tabBarLabel: 'Home',
tabBarIcon: ({ color }) => (
<Icons.IconHome width={ICON_SIZE} height={ICON_SIZE} color={color} />
),
}}
/>
</Tab.Navigator>
</SafeAreaView>
);
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
Any tips on where I'm going wrong? Or am I simply adding the SafeAreaView on the wrong screen?
Without SafeArea it looks like the following:

BottomTab had SafeAreaView so you don't need to wrap it in SafeAreaView.
const BottomTabs = () => (
<Tab.Navigator>
<Tab.Screen
name="Home"
component={FeaturedScreen}
options={{
unmountOnBlur: true,
tabBarLabel: 'Home',
tabBarIcon: ({ color }) => (
<Icons.IconHome width={ICON_SIZE} height={ICON_SIZE} color={color} />
),
}}
/>
</Tab.Navigator>
);
const styles = StyleSheet.create({
container: {
flex: 1,
},
});

Related

How can I make space between the navigation elements in Material Top Tab Navigation?

I have the below code:
const TopNavBar = () => {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={{}}
style={{paddingTop: StatusBar.currentHeight}}>
<Tab.Screen
options={{
title: ({color, focused}) => (
<Icon
size={25}
name={focused ? 'home' : 'home-outline'}
color={focused ? 'blue' : '#272727'}
/>
),
}}
component={Profile}
name="Home"
/>
<Tab.Screen
options={{
title: ({color, focused}) => (
<Icon
size={25}
name={focused ? 'person' : 'person-outline'}
color={focused ? 'blue' : '#272727'}
/>
),
}}
component={Profile}
name="Profile"
/>
</Tab.Navigator>
</NavigationContainer>
);
};
Which outputs:
But what I want is this:
Essentially only the profile button and the drawer only clickable
How can I achieve this?

React Native BottomTabNavigator Icon remove

explanatory photo
I want to remove the 'X' mark in the picture and move it to the alarm part at the top. Is it possible?
Can I move the bottom navigation button to the header?
<Tab.Navigator
screenOptions={{
headerRight: () => (
<NotiIconBtn onPress={() => navigate("Tabs", { screen: "Alarm" })}>
<NotiIcon source={require("../public/img/notiIcon.png")} />
</NotiIconBtn>
),
>
<Tab.Screen
name="MyPage"
component={MyPage}
options={{
headerShown: true,
headerBackTitleVisible: true,
title: "마이페이지",
tabBarIcon: ({ focused, color, size }) => {
return !focused ? (
<>
<Img source={require("../public/img/mypageIcon.png")} />
<Text>마이페이지</Text>
</>
) : (
<>
<Img source={require("../public/img/mypageAIcon.png")} />
<ActiveText>마이페이지</ActiveText>
</>
);
},
}}
/>
skip
.
.
.
<Tab.Screen name="Alarm" component={Alarm} />
</Tab.Navigator>

in DropDownPicker onChangeValue is not triggering

I'm using DropDownPicker in react native but onChangeValue event is not triggering.
I have used onChange and onChangeItem already. Trying to follow this https://hossein-zare.github.io/react-native-dropdown-picker-website/docs/usage#onchangevalue but its not working. Please help me out.
Below is the code:
import React, { useEffect, useState } from 'react';
import {View, Text, Button, TextInput, FlatList, ActivityIndicator, StyleSheet, Image} from 'react-native';
import filter from 'lodash.filter';
import DropDownPicker from 'react-native-dropdown-picker';
const CarList = () => {
const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState([]);
const [error, setError] = useState(null);
const [query, setQuery] = useState('');
const [fullData, setFullData] = useState([]);
const [selected, setSelected] = useState("");
const [open, setOpen] = useState(false);
const [childOpen, setChildOpen] = useState(false);
const [filterOption, setfilteroption] = useState([
{label: 'Color', value: 'Color'},
{label: 'Model', value: 'Model'},
{label: 'Year', value: 'Year'}
]);
const [value, setValue] = useState(null);
const [childvalue, setChildValue] = useState([]);
useEffect(() => {
setIsLoading(true);
fetch(`https://myfakeapi.com/api/cars/?seed=1&page=1&results=20`)
.then(response => response.json())
.then(response => {
setData(response.cars);
setFullData(response.cars);
setIsLoading(false);
})
.catch(err => {
setIsLoading(false);
setError(err);
});
}, []);
if (isLoading) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" color="#5500dc" />
</View>
);
}
if (error) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 18}}>
Error fetching data... Check your network connection!
</Text>
</View>
);
}
const handleSearch = text => {
const formattedQuery = text.toLowerCase();
const filteredData = filter(fullData, user => {
return contains(user, formattedQuery);
});
setData(filteredData);
setQuery(text);
};
const contains = ({ car, car_model,car_color }, query) => {
if (car.toLowerCase().includes(query) || car_model.toLowerCase().includes(query) || car_color.toLowerCase().includes(query)) {
return true;
}
return false;
};
const color = [...new Set(data.map((item) => item.car_color))];
const model = [...new Set(data.map((item) => item.car_model))];
const year = [...new Set(data.map((item) => item.car_model_year))];
//this is not triggering
const changeSelectOptionHandler = (value) => {
console.log("hi")
setSelected(value);
};
if (selected === "Color") {
setChildValue(color)
} else if (selected === "Model") {
setChildValue(model)
} else if (selected === "Year") {
setChildValue(year)
}
function renderHeader() {
return (
<View
style={{
backgroundColor: '#fff',
padding: 10,
marginVertical: 10,
borderRadius: 20
}}
>
<TextInput
autoCapitalize="none"
autoCorrect={false}
clearButtonMode="always"
value={query}
onChangeText={queryText => handleSearch(queryText)}
placeholder="Search"
style={{ backgroundColor: '#fff', paddingHorizontal: 20 }}
/>
<DropDownPicker onChangeItem={changeSelectOptionHandler}
open={open}
value={value}
items={filterOption}
setOpen={setOpen}
setValue={setValue}
setItems={setfilteroption}
dropDownDirection="TOP"
style={{
padding: 5,
margin: 5,
width: 200,
flexDirection: 'row'
// borderRadius: 20
}}
/>
<DropDownPicker
open={childOpen}
items={childvalue}
setOpen={setChildOpen}
setItems={setChildValue}
dropDownDirection="TOP"
/>
</View>
);
}
return (
<View style={styles.container}>
<Text style={styles.text}>Favorite Contacts</Text>
<FlatList
keyboardShouldPersistTaps="always"
ListHeaderComponent={renderHeader}
data={data}
keyExtractor={({ id }) => id}
renderItem={({ item }) => (
<View style={styles.listItem}>
<Image
source={{
uri: 'https://picsum.photos/200',
}}
style={styles.coverImage}
/>
<View style={styles.metaInfo}>
<Text style={styles.title}>{`${item.car} ${
item.car_model
}`}</Text>
<Text>Color: {`${item.car_color}`}</Text>
<Text>Price: {`${item.price}`}</Text>
</View>
</View>
)}
/>
</View>
);
}
I needed to use onSelectItem instead of onChangeValue
The latest version of dropdownpicker
OnChangeValue is not supported.
use this :
onSelectItem={(item) => {console.log(item.value)}}
Note: I am using "react-native-dropdown-picker": "^5.4.2",

How do I render different Screens of an iOS app with React Native and Firebase/Firestore based on a user's role (provider or customer) after login?

I'm creating an app with React Native and Firebase/Firestore, and I need the app to show different screens based on the role of the user's account (Provider or Customer). After a user logs into their account, the app should check their role data from Firebase and render a different screen based on it. So the app will first evaluate the condition to see if they're logged in. Then it will evaluate the condition to see if they're a Provider or Customer and then render the proper screen (either the Login, ProviderHomeScreen, or CustomerHomeScreen).
I've tried to use the condition:
const user = firebase.auth().currentUser;
var role;
if (user != null) {
role = user.role;
console.log(role);
}
if (isLoggedIn) {
if (user.role === 'provider') {
return (
<NavigationContainer>
<ProviderStack.Navigator>
<Stack.Navigator>
<Stack.Screen
name={'Provider Home'}
component={ProviderHomeTabNavigator}
options={{
headerShown: false,
}}
/>
</Stack.Navigator>
</ProviderStack.Navigator>
</NavigationContainer>
);
} else {
return (
<NavigationContainer>
<CustomerStack.Navigator>
<Stack.Screen
name={'Customer Home'}
component={CustomerHomeTabNavigator}
options={{
headerShown: false,
}}
/>
<Stack.Screen
name={'Search'}
component={SearchScreen}
options={{
title: 'Search Availa Providers',
}}
/>
</CustomerStack.Navigator>
</NavigationContainer>
);
}
}
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen
name="Customer Sign-up"
component={CustomerRegistrationScreen}
/>
<Stack.Screen
name="Provider Sign-up"
component={ProviderRegistrationScreen}
/>
</Stack.Navigator>
</NavigationContainer>
but when I console.log the user.role, it says undefined in the console, and the app just renders the CustomerHomeScreen with the CustomerHomeTabNavigator.
Please help me successfully access the role data of the currently signed-in user from Firestore and create conditions to render the proper screens for the proper role of the users.
Right now, this is what my code looks like for App.js:
import 'react-native-gesture-handler';
import React, {useEffect, useState} from 'react';
import {View, Text} from 'react-native';
import {NavigationContainer} from '#react-navigation/native';
import {createNativeStackNavigator} from '#react-navigation/native-stack';
import {createStackNavigator} from '#react-navigation/stack';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import CustomerHomeScreen from '../Availa/src/Customer/screens/Home/index';
import Fontisto from 'react-native-vector-icons/Fontisto';
import FontAwesome from 'react-native-vector-icons/FontAwesome';
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import Feather from 'react-native-vector-icons/Feather';
import AntDesign from 'react-native-vector-icons/AntDesign';
import Ionicons from 'react-native-vector-icons/Ionicons';
import {firebase} from '../Availa/src/firebase/config';
import 'firebase/auth';
import firestore from '#react-native-firebase/firestore';
import 'firebase/firestore';
import 'react-native-gesture-handler';
Fontisto.loadFont();
FontAwesome.loadFont();
Feather.loadFont();
AntDesign.loadFont();
Ionicons.loadFont();
import SearchScreen from '../Availa/src/Customer/screens/SearchScreen/index';
import ProfileScreen from '../Availa/src/Customer/screens/ProfileScreen/index';
import {
LoginScreen,
CustomerRegistrationScreen,
ProviderRegistrationScreen,
} from './src/screens';
import 'firebase/auth';
import {decode, encode} from 'base-64';
if (!global.btoa) {
global.btoa = encode;
}
if (!global.atob) {
global.atob = decode;
}
const ProviderStack = createNativeStackNavigator();
const ProviderStackScreen = () => {
return (
<ProviderStack.Navigator>
<Stack.Navigator>
<Stack.Screen
name={'Home'}
component={ProviderHomeTabNavigator}
options={{
headerShown: false,
}}
/>
</Stack.Navigator>
</ProviderStack.Navigator>
);
};
const ProviderTab = createBottomTabNavigator();
const ProviderHomeTabNavigator = () => {
return (
<Tab.Navigator
tabBarOptions={{
activeTintColor: '#007FFF',
}}>
<Tab.Screen
name={'Provider'}
component={ProviderHomeScreen}
options={{
tabBarIcon: ({color}) => (
<Fontisto name="home" size={25} color={color} />
),
}}
/>
<Tab.Screen
name={'My List'}
component={ProviderHomeScreen}
options={{
tabBarIcon: ({color}) => (
<FontAwesome name="heart-o" size={25} color={color} />
),
}}
/>
<Tab.Screen
name={'Appointments'}
component={ProviderHomeScreen}
options={{
tabBarIcon: ({color}) => (
<AntDesign name="calendar" size={25} color={color} />
),
}}
/>
<Tab.Screen
name={'Messages'}
component={ProviderHomeScreen}
options={{
tabBarIcon: ({color}) => (
<Feather name="message-square" size={25} color={color} />
),
}}
/>
<Tab.Screen
name={'Profile'}
component={ProfileScreen}
options={{
tabBarIcon: ({color}) => (
<Ionicons name="person-circle-outline" size={25} color={color} />
),
}}
/>
</Tab.Navigator>
);
};
const CustomerStack = createNativeStackNavigator();
const CustomerStackScreen = () => {
return (
<CustomerStack.Navigator>
<Stack.Navigator>
<Stack.Screen
name={'Home'}
component={CustomerHomeTabNavigator}
options={{
headerShown: false,
}}
/>
</Stack.Navigator>
</CustomerStack.Navigator>
);
};
const Tab = createBottomTabNavigator();
const CustomerHomeTabNavigator = () => {
return (
<Tab.Navigator
tabBarOptions={{
activeTintColor: '#1cd478',
}}>
<Tab.Screen
name={'Home'}
component={CustomerHomeScreen}
options={{
tabBarIcon: ({color}) => (
<Fontisto name="home" size={25} color={color} />
),
}}
/>
<Tab.Screen
name={'My List'}
component={CustomerHomeScreen}
options={{
tabBarIcon: ({color}) => (
<FontAwesome name="heart-o" size={25} color={color} />
),
}}
/>
<Tab.Screen
name={'Appointments'}
component={CustomerHomeScreen}
options={{
tabBarIcon: ({color}) => (
<AntDesign name="calendar" size={25} color={color} />
),
}}
/>
<Tab.Screen
name={'Messages'}
component={CustomerHomeScreen}
options={{
tabBarIcon: ({color}) => (
<Feather name="message-square" size={25} color={color} />
),
}}
/>
<Tab.Screen
name={'Profile'}
component={ProfileScreen}
options={{
tabBarIcon: ({color}) => (
<Ionicons name="person-circle-outline" size={25} color={color} />
),
}}
/>
</Tab.Navigator>
);
};
const chooseScreen = user => {
if (user?.role === 'provider') {
ProviderStackScreen();
}
CustomerStackScreen();
};
const Stack = createStackNavigator();
export default function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [loading, setLoading] = useState(true);
const firestore = firebase.firestore;
const auth = firebase.auth;
const user = firebase.auth().currentUser;
// //to check if Firebase has been initialized
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
} else {
firebase.app();
}
firebase.auth().onAuthStateChanged(user => {
if (user != null) {
setIsLoggedIn(true);
} else {
setIsLoggedIn(false);
}
});
var role;
if (user != null) {
role = user.role;
console.log(role);
}
if (isLoggedIn) {
if (user.role === 'provider') {
return (
<NavigationContainer>
<ProviderStack.Navigator>
<Stack.Navigator>
<Stack.Screen
name={'Provider Home'}
component={ProviderHomeTabNavigator}
options={{
headerShown: false,
}}
/>
</Stack.Navigator>
</ProviderStack.Navigator>
</NavigationContainer>
);
} else {
return (
<NavigationContainer>
<CustomerStack.Navigator>
<Stack.Screen
name={'Customer Home'}
component={CustomerHomeTabNavigator}
options={{
headerShown: false,
}}
/>
<Stack.Screen
name={'Search'}
component={SearchScreen}
options={{
title: 'Search Availa Providers',
}}
/>
</CustomerStack.Navigator>
</NavigationContainer>
);
}
}
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen
name="Customer Sign-up"
component={CustomerRegistrationScreen}
/>
<Stack.Screen
name="Provider Sign-up"
component={ProviderRegistrationScreen}
/>
</Stack.Navigator>
</NavigationContainer>
);
Also, this is what my LoginScreen.js looks like:
import React, {useState} from 'react';
import {Image, Text, TextInput, TouchableOpacity, View} from 'react-native';
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
import styles from './styles';
import {firebase} from '../../firebase/config';
export default function LoginScreen({navigation}) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const onLoginPress = () => {
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(response => {
const uid = response.user.uid;
const usersRef = firebase.firestore().collection('users');
usersRef
.doc(uid)
.get()
.then(firestoreDocument => {
if (!firestoreDocument.exists) {
alert('User does not exist');
return;
}
const user = firestoreDocument.data();
if (user.role === 'provider') {
navigation.navigate('Provider Home', {user});
} else {
navigation.navigate('Customer Home', {user});
}
})
.catch(error => {
alert(error);
});
})
.catch(error => {
alert(error);
});
};
return (
<View style={styles.container}>
<KeyboardAwareScrollView
style={{flex: 1, width: '100%'}}
keyboardShouldPersistTaps="always">
<Image
style={styles.logo}
source={require('../../../assets/availalogo.png')}
/>
<TextInput
style={styles.input}
placeholder="Email"
placeholderTextColor="#aaaaaa"
onChangeText={text => setEmail(text)}
value={email}
underlineColorAndroid="transparent"
autoCapitalize="none"
/>
<TextInput
style={styles.input}
placeholderTextColor="#aaaaaa"
secureTextEntry
placeholder="Password"
onChangeText={text => setPassword(text)}
value={password}
underlineColorAndroid="transparent"
autoCapitalize="none"
/>
<TouchableOpacity style={styles.button} onPress={() => onLoginPress()}>
<Text style={styles.buttonTitle}>Log in</Text>
</TouchableOpacity>
<Text style={styles.footerText}>Don't have an account?</Text>
<TouchableOpacity
style={{
backgroundColor: '#ED2939', //#118C4F?
marginLeft: 30,
marginRight: 30,
marginTop: 20,
height: 48,
borderRadius: 5,
alignItems: 'center',
justifyContent: 'center',
}}
onPress={() => navigation.navigate('Provider Sign-up')}>
<Text style={{color: 'white', fontSize: 16, fontWeight: 'bold'}}>
Sign Up as Availa Provider
</Text>
</TouchableOpacity>
<TouchableOpacity
style={{
backgroundColor: '#007FFF',
marginLeft: 30,
marginRight: 30,
marginTop: 20,
height: 48,
borderRadius: 5,
alignItems: 'center',
justifyContent: 'center',
}}
onPress={() => navigation.navigate('Customer Sign-up')}>
<Text style={{color: 'white', fontSize: 16, fontWeight: 'bold'}}>
Sign Up as Customer
</Text>
</TouchableOpacity>
</KeyboardAwareScrollView>
</View>
);
}
Are you adding the role in custom claims?
if so you need to get the the claims from getIdTokenResult
firebase.auth().currentUser.getIdTokenResult()
.then((idTokenResult) => {
const role = idTokenResult.claims.role;
})
.catch((error) => {
console.log(error);
});
Also I personally keep the firebase initialization outside of the component and
firebase.auth().onAuthStateChanged(user => {
if (user != null) {
setIsLoggedIn(true);
} else {
setIsLoggedIn(false);
}
});
inside useEffect, so instead of setIsLoggedIn would do setUser and in your case after you get the role you can set it in a different state.

White line above BottomTabNavigator in react-native

I just started developing an app in react-native and added a bottom navigation. I then started styling the components and noticed a white line above the navigation, which i simply can't get rid of.
Picture of the Problem
Any idea on how to make that line have the same color as the background would be appreciated. It might be possible that the default background color behind the views is "shining through" since it's white and I have no idea how to change it. The app is only supposed to work on my own iPhone XR, so I'm not concerned about compatibility with android or other iPhone models
I'm a complete beginner regarding react-native, so bear with me here.
Here is my code so far:
Navigation
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
tabBarOptions={{
activeTintColor: Colors.tabIconSelected,
inactiveTintColor: Colors.tabIconDefault,
style: styles.container
}}>
<Tab.Screen
name="Payments"
component={PaymentScreen}
options={{
tabBarIcon: ({focused}) => <TabBarIcon focused={focused} name="logout"/>
}}/>
<Tab.Screen
name="Income"
component={IncomeScreen}
options={{
tabBarIcon: ({focused}) => <TabBarIcon focused={focused} name="login"/>
}}/>
</Tab.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: Colors.darkBackgroundColor,
}
});
Payment View
export default class PaymentScreen extends Component{
render() {
return (
<View style={styles.container}>
<Text style={styles.text}>Payments!</Text>
</View>
)
}
}
const styles = StyleSheet.create({
container:{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: Colors.backgroundColor,
},
text:{
color: Colors.textColor
}
});
Edit: the API has changed since I posted this answer. See the solution below if this one doesn't work for you
Figured it out myself after a bit of trial and error. The Tab.Navigator class of the NavigationContainer has a prop called tabBarOptions which takes a stylesheet as its style option. And of course the border of the component can be changed here as well.
Here's the catch though: setting borderWidth to 0 does not hide the white border above the navigation. Only setting borderTopWidth to 0 has the desired effect.
So the full solution looks like this:
<NavigationContainer>
<Tab.Navigator
tabBarOptions={{
activeTintColor: Colors.tabIconSelected,
inactiveTintColor: Colors.tabIconDefault,
style: styles.container
}}/>
</NavigationContainer>
const styles = StyleSheet.create({
container: {
backgroundColor: Colors.darkBackgroundColor,
borderTopWidth: 0
}
});
You can remove it in tabBarStyle in screenOption props likes below
<Tab.Navigator
screenOptions={
tabBarStyle:{borderTopWidth:0}
}
/>
Using React Navigation 6.x
In my case, I had to put the tabBarStyle on every Tab.Screen so for example:
<Tab.Screen name="HomeTab" component={xx} options={({ route }) => ({
title: 'Home',
tabBarStyle: {borderTop: 0},
})} />
<Tab.Screen name="SecondTab" component={xx} options={({ route }) => ({
title: 'SecondTab',
tabBarStyle: {borderTop: 0},
})} />
<Tab.Screen name="ThirdTab" component={xx} options={({ route }) => ({
title: 'ThirdTab',
tabBarStyle: {borderTop: 0},
})} />
(unable to comment this answer as link is too much lengthy)
This line cannot be removed it seems. It'a coming by default when BottomTab is rendered to differentiate BottomTab and rest of the screen. Go through this example and try to apply multiple background colors for `BottomTab' then you will understand a bit clearly.
in Bottom Tabs Navigator v6,
you can use tabBarStyle prop to resolve this white line issue.
make sure to gave the borderTopWidth:0,
screenOptions={({route}) =>({
tabBarStyle: {
height: IS_IPHONE ? RFValue(47) : RFValue(42),
backgroundColor: AppTheme.colors.darkGrey,
borderTopWidth: 0,
},
}
)
}

Resources