TabBar with floating action button in React Native - javascript

I was following this tutorial where they created a TaBar with a Floating Button, and after adding the code, it seems to work really well on iOS, as shown below:
On Android, it is a different case; it appears the floating button gets cropped right where the BottomBar ends, as shown below:
I do not understand why this is happening, below is the code
import React from "react";
import { StyleSheet, View } from "react-native";
import {
BottomTabBar,
createBottomTabNavigator,
} from "#react-navigation/bottom-tabs";
import { FontAwesome as Icon } from "#expo/vector-icons";
import { TabBarAdvancedButton } from "./TabBarAdvancedButton";
import { isIphoneX } from "src/utils/deviceHelpers";
import { DARKTEXT, WHITE } from "src/styles/colors";
import { BlankScreen } from "src/screens";
import { Badge } from "react-native-paper";
const BottomBar = createBottomTabNavigator();
type Props = {
barColor?: string;
isBtnDisabled?: boolean;
hasNotification?: boolean;
};
export const TabBar: React.FC<Props> = ({
barColor = WHITE,
isBtnDisabled = false,
hasNotification = false,
...props
}) => {
return (
<BottomBar.Navigator
backBehaviour="initialRoute"
name="BarStack"
tabBar={(props) => (
<View style={styles.navigatorContainer}>
<BottomTabBar {...props} />
{isIphoneX() && (
<View
style={[
styles.xFillLine,
{
backgroundColor: barColor,
},
]}
/>
)}
</View>
)}
tabBarOptions={{
showIcon: true,
style: styles.navigator,
tabStyle: {
backgroundColor: barColor,
},
}}
>
<BottomBar.Screen
name="Home"
component={BlankScreen}
options={{
tabBarIcon: ({ color }) => (
<Icon name="home" size={24} color={color} />
),
}}
/>
<BottomBar.Screen
name="Lists"
component={BlankScreen}
options={{
tabBarIcon: ({ color }) => (
<Icon name="file" size={24} color={color} />
),
}}
/>
<BottomBar.Screen
name="Rocket"
component={BlankScreen}
options={{
tabBarButton: (props) => (
<TabBarAdvancedButton bgColor={barColor} {...props} />
),
}}
/>
<BottomBar.Screen
name="Alerts"
component={BlankScreen}
options={{
tabBarIcon: ({ color }) => (
<View>
<Icon name="bell" size={24} color={color} />
{hasNotification && <Badge />}
</View>
),
}}
/>
<BottomBar.Screen
name="search"
component={BlankScreen}
options={{
tabBarIcon: ({ color }) => (
<Icon name="search" size={24} color={color} />
),
}}
/>
</BottomBar.Navigator>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
navigatorContainer: {
position: "absolute",
bottom: 0,
left: 0,
right: 0,
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 1,
},
shadowOpacity: 0.22,
shadowRadius: 2.22,
},
navigator: {
borderTopWidth: 0,
backgroundColor: "transparent",
elevation: 30,
},
tabIconStyle: {
color: DARKTEXT,
},
xFillLine: {
position: "absolute",
bottom: 0,
left: 0,
right: 0,
height: 34,
},
});
export default TabBar;
And the Floating button:
import React from 'react';
import { StyleSheet, TouchableOpacity, View } from 'react-native';
import { BottomTabBarButtonProps } from '#react-navigation/bottom-tabs/lib/typescript/src/types';
import { FontAwesome as Icon } from '#expo/vector-icons';
import { TabBg } from './svg';
type Props = BottomTabBarButtonProps & {
bgColor?: string;
};
export const TabBarAdvancedButton: React.FC<Props> = ({
bgColor,
...props
}) => (
<View
style={styles.container}
pointerEvents="box-none"
>
<TabBg
color={bgColor}
style={styles.background}
/>
<TouchableOpacity
style={styles.button}
onPress={props.onPress}
>
<Icon
name="rocket"
style={styles.buttonIcon}
/>
</TouchableOpacity>
</View>
);
const styles = StyleSheet.create({
container: {
position: 'relative',
width: 75,
alignItems: 'center',
},
background: {
position: 'absolute',
top: 0,
},
button: {
top: -22.5,
justifyContent: 'center',
alignItems: 'center',
width: 50,
height: 50,
borderRadius: 27,
backgroundColor: '#E94F37',
},
buttonIcon: {
fontSize: 16,
color: '#F6F7EB'
}
});

Related

How to navigate to a screen in parent navigation in React native

I am very new to React Navigation and am trying to implement the following functionality in react-native:
I have a stack navigator as a parent and a bottom navigation bar as its child. From the home screen, when the user clicks the logout option, they should return to the Sign In screen which is a part of the parent navigation.
There are already a lot of questions regarding this, but I am not able to implement the previous solutions in my code.
Someone please help me out, the code is given below (This is an Expo managed project):
Navigation Components
import { NavigationContainer } from "#react-navigation/native";
import { createNativeStackNavigator } from "#react-navigation/native-stack";
import WelcomeScreen from "../screens/WelcomeScreen";
import Features from "../screens/Features";
import SignIn from "../screens/SignIn";
import Register from "../screens/Register";
import { createBottomTabNavigator } from "#react-navigation/bottom-tabs";
import { Icon } from "react-native-elements";
import Home from "../screens/Home";
import Reminders from "../screens/Reminders";
import Settings from "../screens/Settings";
const BottomNavbar = () => {
const Tab = createBottomTabNavigator();
return (
<Tab.Navigator
initialRouteName="Home"
screenOptions={({ route }) => ({
tabBarIcon: ({ focused }) => {
let iconName;
let rn = route.name;
if (rn === "Home") {
iconName = focused ? "home" : "home-outline";
} else if (rn === "Reminders") {
iconName = focused ? "list" : "list-outline";
} else if (rn === "Settings") {
iconName = focused ? "settings" : "settings-outline";
}
return (
<Icon
name={iconName}
size={25}
color="black"
type="ionicon"
/>
);
},
})}
showLabel
>
<Tab.Screen
name="Home"
component={Home}
options={{ headerShown: false }}
/>
<Tab.Screen
name="Reminders"
component={Reminders}
options={{ headerShown: false }}
/>
<Tab.Screen
name="Settings"
component={Settings}
options={{ headerShown: false }}
/>
</Tab.Navigator>
);
};
const Navigator = () => {
const Stack = createNativeStackNavigator();
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{ headerShown: false }}
initialRouteName="Welcome"
>
<Stack.Screen name="Welcome" component={WelcomeScreen} />
<Stack.Screen name="Features" component={Features} />
<Stack.Screen name="SignIn" component={SignIn} />
<Stack.Screen name="Register" component={Register} />
<Stack.Screen name="BottomNavbar" component={BottomNavbar} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default Navigator;
Home Screen Components
import {
View,
Text,
SafeAreaView,
StyleSheet,
TouchableOpacity,
ScrollView,
} from "react-native";
import React from "react";
import { Header, Image, Icon } from "react-native-elements";
import { useFonts } from "expo-font";
import ServiceCard from "../components/ServiceCard";
import PetCard from "../components/PetCard";
const SignOut = ({ navigation }) => {
return (
<TouchableOpacity
onPress={() => {
navigation.navigate("SignIn");
}}
>
<Icon name="logout" color="black" size={20} />
</TouchableOpacity>
);
};
const Home = () => {
const [loaded, error] = useFonts({
Montserrat: require("../assets/fonts/Montserrat-Regular.ttf"),
});
if (!loaded) {
return null;
}
const url1 =
"https://images.unsplash.com/photo-1530281700549-e82e7bf110d6?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=388&q=80";
const url2 =
"https://images.unsplash.com/photo-1560807707-8cc77767d783?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=435&q=80";
const url3 =
"https://images.unsplash.com/photo-1543466835-00a7907e9de1?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=774&q=80";
return (
<View style={styles.screen}>
<Header
leftComponent={{
icon: "menu",
color: "black",
}}
rightComponent={<SignOut />}
centerComponent={{
text: "PetPapa",
color: "black",
style: {
fontFamily: "Montserrat",
fontSize: 20,
},
}}
barStyle="dark-content"
backgroundColor="white"
containerStyle={styles.header}
/>
<View style={styles.community}>
<View style={styles.commOffer}>
<View>
<Text style={styles.commOfferTitle}>Join our</Text>
<Text style={styles.commOfferTitle}>
community today!
</Text>
</View>
<TouchableOpacity style={styles.btn}>
<Text style={styles.commOfferJoin}>Join Now</Text>
</TouchableOpacity>
</View>
<Image
source={{
uri: "https://imgur.com/nB4Xm1Z.png",
}}
style={styles.commDog}
/>
</View>
<View style={styles.listView}>
<View style={styles.topText}>
<Text style={styles.title}>Services</Text>
<TouchableOpacity>
<Text style={styles.option}>See more</Text>
</TouchableOpacity>
</View>
<ServiceCard />
</View>
<View style={styles.listView}>
<View style={styles.topText}>
<Text style={styles.title}>My Pets</Text>
<TouchableOpacity>
<Text style={styles.option}>See all</Text>
</TouchableOpacity>
</View>
<ScrollView
style={styles.petView}
horizontal={true}
showsHorizontalScrollIndicator={true}
persistentScrollbar={true}
>
<PetCard name="Miles" Img={url1} />
<PetCard name="Jack" Img={url2} />
<PetCard name="Ellie" Img={url3} />
</ScrollView>
</View>
</View>
);
};
const styles = StyleSheet.create({
screen: {
height: "100%",
backgroundColor: "white",
alignItems: "center",
},
header: {
backgroundColor: "white",
height: 80,
},
community: {
backgroundColor: "#1976D2",
height: "15%",
width: "80%",
borderRadius: 20,
marginTop: 50,
flexDirection: "row",
justifyContent: "space-around",
},
commDog: {
marginTop: 10,
marginRight: 15,
height: 105,
width: 75,
},
commOffer: {
marginTop: 10,
flexDirection: "column",
justifyContent: "space-around",
},
commOfferTitle: {
color: "white",
fontFamily: "Montserrat",
fontSize: 16,
},
btn: {
backgroundColor: "#FFC107",
width: "50%",
borderRadius: 10,
},
commOfferJoin: {
color: "white",
margin: 5,
},
listView: {
marginTop: 50,
width: "80%",
},
topText: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
title: {
fontFamily: "Montserrat",
fontWeight: "600",
},
option: {
fontFamily: "Montserrat",
opacity: 0.4,
},
block: {
backgroundColor: "#FF5677",
width: 75,
height: 100,
justifyContent: "center",
alignItems: "center",
marginTop: 25,
borderRadius: 20,
},
petView: {
width: "100%",
backgroundColor: "white",
height: 250,
},
});
export default Home;
My Directory Structure:
First you need to add navigation prop to Home screen component
const Home = ({navigation}) => {
const [loaded, error] = useFonts({
Montserrat: require("../assets/fonts/Montserrat-Regular.ttf"),
});
...
then You need to pass navigation prop to the signout component
<Header
leftComponent={{
icon: "menu",
color: "black",
}}
rightComponent={<SignOut navigation={navigation} />}
...
You can also use useNavigation hook from react navigation
import { useNavigation } from '#react-navigation/native';
const SignOut = ({}) => {
const navigation = useNavigation()
return (
<TouchableOpacity
onPress={() => {
navigation.navigate("SignIn");
}}
>
<Icon name="logout" color="black" size={20} />
</TouchableOpacity>
);
};
If You want to create login flow then you should use Authentication flow which I think best prectice and recommended way
The problem in current flow if you logout and navigate to sign-in page once then if you navigate back from you app then as behaviour of stack navigation it just pop current screen and it will again navigate to home screen which is not what supposed to be correct.
You can learn it from react-navigation offical doc
https://reactnavigation.org/docs/auth-flow/
if you want a video tutorial how to implement Authentication flow using redux(state management lib) then I have video tutorial you can learn this video -> https://youtu.be/cm1oJ7JmW6c

Improving React Native performance deactivating Component when not in use

I'm developing an app for tuning pianos in react-native. I have an App.js and some screens implemented with react-navigation. The question is, how i can deactivate (or just not render) a component Screen when the user isn't in that screen?
The idea is to catch when this Screen Component is the Active Screen (like using navigation props), i can activate the render and deactivate when the Active Screen changes.
The Tab.js, containing the tab screens.
import React from "react";
/** Screens */
import TunerScreen from "./tunerScreen";
import Inharmonicity from "./inharmonicityScreen";
import BeatsScreen from "./beatsScreen";
/** Navigation */
import { MaterialCommunityIcons } from "#expo/vector-icons";
import { createMaterialBottomTabNavigator } from "#react-navigation/material-bottom-tabs";
/** Tab */
const Tab = createMaterialBottomTabNavigator();
/* Tabs of screen */
export default Tabs = ({
handleSwitch,
beatsCalc,
inharmonicityCalc,
inharmonicitySave,
}) => {
return (
<Tab.Navigator activeColor="#000" barStyle={{ backgroundColor: "white" }}>
<Tab.Screen
...
/>
// This is the component screen trying to deactivate
<Tab.Screen
name="Beats"
children={() => (
<BeatsScreen beatsCalc={beatsCalc} />
)}
options={{
tabBarLabel: "Beats",
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons
name="blur-radial"
color="black"
size={26}
/>
),
}}
/>
<Tab.Screen
...
/>
</Tab.Navigator>
);
};
The BeatsScreen.js (component to deactivate).
import React, { Component, useState } from "react";
import { View, Text, Button, Switch } from "react-native";
import Note from "../src/components/note";
import { connect } from "react-redux";
import style from "../styles/style";
import Picker from "#gregfrench/react-native-wheel-picker";
var PickerItem = Picker.Item;
class BeatsScreen extends Component {
constructor(props) {
super(props);
this.state = {
beats: 0,
// Prima nota
firstNote: { name: "A", octave: 4, frequency: 440, selected: false },
// Seconda nota
secondNote: { name: "A", octave: 4, frequency: 440, selected: false },
// Elemento selezionato dal picker
selectedItem: 0,
// Elementi del picker
itemList: ["Terza", "Quarta", "Quinta", "Ottava", "Prima"],
};
}
beatsUpdate = () => {
this.state.beats = beatsCalc(
this.state.firstNote,
this.state.secondNote,
this.state.selectedItem
);
};
render() {
if (!this.state.firstNote.selected)
this.state.firstNote = this.props.note;
if (!this.state.secondNote.selected)
this.state.secondNote = this.props.note;
this.beatsUpdate();
return (
<View style={{ flex: 1, flexDirection: "column" }}>
<View style={{ flex: 1 }}>
<View style={{ flex: 1, flexDirection: "row" }}>
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<Note {...this.state.firstNote} />
<Text style={style.frequency}>
{this.state.firstNote.frequency.toFixed(1)} Hz
</Text>
{
<Switch
style={{
paddingTop: 50,
transform: [{ scaleX: 2 }, { scaleY: 2 }],
}}
value={this.state.firstNote.selected}
onValueChange={() => {
// Se la nota è stata selezionata
this.state.firstNote.selected =
!this.state.firstNote.selected;
}}
/>
}
</View>
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<Note {...this.state.secondNote} />
<Text style={style.frequency}>
{this.state.secondNote.frequency.toFixed(1)} Hz
</Text>
<Switch
style={{
paddingTop: 50,
transform: [{ scaleX: 2 }, { scaleY: 2 }],
}}
value={this.state.secondNote.selected}
onValueChange={() => {
// Se la nota è stata selezionata
this.state.secondNote.selected =
!this.state.secondNote.selected;
}}
/>
</View>
</View>
</View>
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<Picker
style={{ width: 150, height: 180 }}
lineColor="#000000" //to set top and bottom line color (Without gradients)
lineGradientColorFrom="#008000" //to set top and bottom starting gradient line color
lineGradientColorTo="#FF5733" //to set top and bottom ending gradient
selectedValue={this.state.selectedItem}
itemStyle={{ color: "black", fontSize: 26 }}
onValueChange={(index) => {
this.state.selectedItem = index;
this.beatsUpdate();
}}
>
{this.state.itemList.map((value, i) => (
<PickerItem label={value} value={i} key={i} />
))}
</Picker>
<Text style={{ fontSize: 18 }}>Battimenti: </Text>
<Text style={{ fontSize: 80, paddingTop: 1 }}>
{this.state.beats}
</Text>
</View>
</View>
);
}
}
/** Mappa lo stato (store) con le props, cosi' da poterle usare nel componente. */
const mapStateToProps = (state) => {
const { note } = state;
const { tunerSwitch } = state;
return { note, tunerSwitch };
};
const mapDispatchToProps = (dispatch) => {
return {
startAndStop: () => dispatch({ type: "SWITCH" }),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(BeatsScreen);
I think if you use this option on the init of the tab bar you could solve your issue https://reactnavigation.org/docs/bottom-tab-navigator/#detachinactivescreens

React navigation 5 tabBarVisible not completely hidden

I added a button in tabBarIcon and it is not completely hidden when using tabBarVisible,my code:
file: App.js
i'm just added a different icon in AddScreen.
import HomeStack from './stacks/Home';
import UserStack from './stacks/User';
import AddStack from './stacks/Add';
import AddIcon from './AddIcon';
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen
name="Home"
component={HomeStack}
options={{
tabBarIcon: () => <IconAnt name={'home'} style={styles.icon} />,
}}
/>
<Tab.Screen
name="Add"
component={AddStack}
options={{
tabBarIcon: () => <AddIcon />, // icon add
title: '',
}}
/>
<Tab.Screen
name="User"
component={UserStack}
options={{
tabBarIcon: () => <IconAnt name={'user'} style={styles.icon} />,
}}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
file: stacks/Home.js
when press 'go to detail', I'm hide the tabbar
function HomeScreen({navigation}) {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Home!</Text>
<TouchableOpacity onPress={() => navigation.navigate('Detail')}>
<Text>go to detail/Text>
</TouchableOpacity>
</View>
);
}
function DetailScreen() {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Detail!</Text>
</View>
);
}
const HomeStack = createStackNavigator();
export default function ({navigation, route}) {
var tabBarVisible = true;
if (typeof route.state !== 'undefined') {
const {routes} = route.state;
if (routes.length > 1) {
tabBarVisible = false;
}
}
navigation.setOptions({tabBarVisible});
// tabbar will hide when moving to Detail screen
return (
<HomeStack.Navigator>
<HomeStack.Screen name={'Home'} component={HomeScreen} />
<HomeStack.Screen name={'Detail'} component={DetailScreen} />
</HomeStack.Navigator>
);
}
AddIcon.js
file icon of AddScreen
export default function () {
return (
<View style={styles.container}>
<View style={styles.button}>
<IconAnt name={'plus'} style={{fontSize: 30, color: '#fff'}} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
alignItems: 'center',
zIndex: 99,
},
button: {
alignItems: 'center',
justifyContent: 'center',
width: 72,
height: 72,
borderRadius: 72 / 2,
backgroundColor: 'red',
position: 'absolute',
marginTop: -45,
shadowColor: 'red',
shadowRadius: 5,
shadowOffset: {height: 10},
shadowOpacity: 0.3,
borderWidth: 2,
borderColor: '#FFFFFF',
zIndex: 99,
},
});
Screenshot:
Home screen
Detail screen
How do I hide it completely? Thanks!
Actually your tab bar is hiding but as you place this on above of the tabs that's why you still able to see after making tabBarVisible = false you also have to hide it manually with styles while making tabBarVisible = false, Check the route and apply styles dynamically.
pass style object to tabBarOptions
tabBarOptions: {
style: {
opacity: 0
}
}
Or this may also work
tabBarOptions: {
style: {
top: 50
}
}
Edit: By looking into your code I had to change some structure of your navigator and now it's working in the snack, You were rendering stack inside the tabs but it should be tabs inside a stack.
Link to snack
I just faced the same issue when I used the custom image.
Here is my solution.
You have to find the index of the Tab you want to hide. Then you can handle the opacity of the tab bar by checking the index.
tabBarOptions={{
style: {...styles.navigator, opacity: routeIndex === 2 ? 0 : 1},
}}
const HomeTab = createBottomTabNavigator();
export const HomeTabScreens = ({route}) => {
const routeIndex = route.state.index; // 2 is the screen that I want to hide.
...
}
you can add focused prop in tabBarIcon and apply style accordingly :
tabBarIcon: ({focused) => {
<Image
source={InstaCircleIcon}
style={[
{width: 87, height: 87},
focused && {width: 0, height: 87},
]}
/>
}
Complete example
<Tab.Screen
name="Addpost"
component={Addpost}
options={{
tabBarLabel: '',
tabBarIcon: ({focused}) => (
<TouchableOpacity style={[{top: -30, borderRadius: 50}]}>
<Image
source={InstaCircleIcon}
style={[
{width: 87, height: 87},
focused && {width: 0, height: 87},
]}
/>
</TouchableOpacity>
),
tabBarVisible: false,
}}
/>

DrawerNavigation - Can't change header colour

I'm working on a react native application, and using a DrawerNavigator. Unfortunately I'm not able to change the colour of the header, my SideMenu component looks like this:
import React from 'react';
import {
TouchableHighlight,
View,
ScrollView,
Image,
Platform,
StyleSheet
} from 'react-native';
import {NavigationActions} from 'react-navigation';
import {
RkStyleSheet,
RkText,
RkTheme
} from 'react-native-ui-kitten';
import { MainRoutes } from '../routes/routes';
import { MaterialCommunityIcons } from 'react-native-vector-icons';
import { connect } from 'react-redux';
const mapStateToProps = (state) => {
return {
}
}
class SideMenu extends React.Component {
static navigationOptions = ({navigation}) => {
const { state, setParams } = navigation;
return {
headerTintColor: 'red',
headerLeft: null,
headerStyle: { backgroundColor: 'rgba(77, 90, 139, 1)', shadowColor: 'transparent', borderBottomWidth: 0},
};
};
constructor(props) {
super(props);
this._navigateAction = this._navigate.bind(this);
}
_navigate(route) {
let resetAction = NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({routeName: route.id})
]
});
this.props.navigation.dispatch(resetAction)
}
_renderIcon() {
// if (RkTheme.current.name === 'light')
// return <Image style={styles.icon} source={require('../../assets/images/smallLogo.png')}/>;
// return <Image style={styles.icon} source={require('../../assets/images/smallLogoDark.png')}/>
}
handlePress = (route, index) => {
const { navigation } = this.props;
navigation.navigate(route.id);
}
render() {
let menu = MainRoutes.map((route, index) => {
return (
<TouchableHighlight
style={styles.container}
key={route.id}
underlayColor={RkTheme.current.colors.button.underlay}
activeOpacity={1}
onPress={() => this.handlePress(route, index)}>
<View style={styles.content}>
<View style={styles.content}>
<MaterialCommunityIcons size={25} style={{color: 'white'}} name={route.icon} />
<View style={{flex: 1, alignItems: 'left', paddingLeft: 15}}>
<RkText style={{color:'white'}}>{route.title}</RkText>
</View>
</View>
</View>
</TouchableHighlight>
)
});
return (
<View style={styles.root}>
<ScrollView
showsVerticalScrollIndicator={false}>
<View style={[styles.container, styles.content], {borderTopWidth: 0}}>
{this._renderIcon()}
</View>
{menu}
</ScrollView>
</View>
)
}
}
let styles = RkStyleSheet.create(theme => ({
container: {
height: 80,
paddingHorizontal: 16,
borderTopWidth: StyleSheet.hairlineWidth,
borderColor: 'white',
backgroundColor: 'rgba(77, 90, 139, 1)'
},
root: {
paddingTop: Platform.OS === 'ios' ? 20 : 0,
backgroundColor: 'rgba(77, 90, 139, 1)'
},
content: {
flex: 1,
flexDirection: 'row',
alignItems: 'center'
},
icon: {
marginRight: 13,
}
}));
export default connect(mapStateToProps)(SideMenu);
As you can see I do set the style of the header in the NavigationOptions like I do with the other components but the header stays the same colour.
Could this be because the DrawerNavigator is nested within a TabNavigator?
Thanks and really appreciate any help.
The Navigators are defined as so:
onst SettingsDrawerNavigator = DrawerNavigator(
{
SettingsScreen: {
screen: SettingsScreen
}
},
{
//initialRouteName: 'SettingsScreen',
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle',
contentComponent: (props) => <SideMenu {...props}/>
}
);
export default TabNavigator(
//Adds elements to the navigator at the bottom.
{
//Other tabs.
Account: {
screen: SettingsDrawerNavigator,
}
},
{
navigationOptions: ({ navigation }) => ({
initialRouteName: 'Home',
tabBarIcon: ({ focused }) => {
const { routeName } = navigation.state;
let iconName;
return (
// <Button badge vertical>
// <Badge ><Text>51</Text></Badge>
<Ionicons
name={iconName}
size={24}
style={{ marginBottom: -3 }}
color={focused ? Colors.tabIconSelected : Colors.tabIconDefault}
/>
// </Button>
);
},
}),
tabBarOptions: {
inactiveBackgroundColor: 'transparent',
activeBackgroundColor: 'transparent',
showLabel: false,
style: {
backgroundColor: '#4d5a8b',
}
},
tabBarComponent: TabBarBottom,
tabBarPosition: 'bottom',
animationEnabled: false,
swipeEnabled: false
}
);
Adding a header to the DrawerNavigator resulted in the following (red). I'm trying to set the white background at the top to red.

How can I change the present image of the button to another image on button press in react-native

In my home screen of the app, I have two buttons, If the user selects a button the touched response is to be shown by changing its present image into another I tried all the methods of setting state and passing conditions but it's not working
Here is my code MainScreen.js file:
import React, { Component } from 'react';
import { StatusBar, Image, TouchableHighlight } from 'react-native';
import { Actions } from 'react-native-router-flux';
class MainScreen extends Component {
state = {
computerPressed: false,
teamPressed: true
}
render() {
return (
<Image
source={require('./Images/bg_img.png')}
style={styles.backgroundStyle} >
<StatusBar hidden />
<Image
source={require('./Images/History.png')}
style={styles.historybuttonStyle} />
<Image
source={require('./Images/logo_ws.png')}
style={styles.logoStyle} />
<TouchableHighlight onPress={() => Actions.screen2({ challenge: 'Computer' })}
onPressIn={this.state.computerPressed = true}
onPressOut={this.state.computerPressed}>
<Image
source={require(this.state.computerPressed ? './Images/landing-bnt1-on.png' : './Images/landing-bnt1.png')}
style={styles.landingbnt1Style} />
</TouchableHighlight>
<TouchableHighlight onPress={() => Actions.screen2({ challenge: 'Team' })}
onPressIn={this.state.teamPressed = true}
onPressOut={this.state.teamPressed}>
<Image
source={require(this.state.computerPressed ? './Images/landing-bnt2-on.png' : './Images/landing-bnt2.png')}
style={styles.landingbnt2Style} />
</TouchableHighlight>
</Image>
);
}
}
const styles = {
backgroundStyle: {
flex: 1,
width: undefined,
height: undefined,
justifyContent: 'center',
alignItems: 'center',
flexWrap: 'wrap',
position: 'relative'
},
logoStyle: {
flex: 0,
width: 340,
height: 270,
resizeMode: 'contain',
marginBottom: 150,
position: 'absolute',
bottom: 50
},
historybuttonStyle: {
width: 38,
height: 35,
position: 'absolute',
right: 5,
top: 10
},
landingbnt1Style: {
width: 250,
height: 45,
top: 175
},
landingbnt2Style: {
width: 250,
height: 45,
top: 200
}
};
export default MainScreen;
and I have this error pop up when I executed this code:
and when i changed my code to this:
import React, { Component } from 'react';
import { StatusBar, Image, TouchableHighlight } from 'react-native';
import { Actions } from 'react-native-router-flux';
const a = require('./Images/landing-bnt1-on.png');
const b = require('./Images/landing-bnt1.png');
const c = require('./Images/landing-bnt2-on.png');
const d = require('./Images/landing-bnt2.png');
class MainScreen extends Component {
state = {
computerPressed: false,
teamPressed: true
}
render() {
return (
<Image
source={require('./Images/bg_img.png')}
style={styles.backgroundStyle} >
<StatusBar hidden />
<Image
source={require('./Images/History.png')}
style={styles.historybuttonStyle} />
<Image
source={require('./Images/logo_ws.png')}
style={styles.logoStyle} />
<TouchableHighlight
onPress={() => Actions.screen2({ challenge: 'Computer' })}
onPressIn={this.state.computerPressed = true}
onPressOut={this.state.computerPressed}>
<Image
source={require(this.state.computerPressed ? a : b)}
style={styles.landingbnt1Style} />
</TouchableHighlight>
<TouchableHighlight
onPress={() => Actions.screen2({ challenge: 'Team' })}
onPressIn={this.state.teamPressed = true}
onPressOut={this.state.teamPressed}>
<Image
source={require(this.state.computerPressed ? c : d)}
style={styles.landingbnt2Style} />
</TouchableHighlight>
</Image>
);
}
}
const styles = {
backgroundStyle: {
flex: 1,
width: undefined,
height: undefined,
justifyContent: 'center',
alignItems: 'center',
flexWrap: 'wrap',
position: 'relative'
},
logoStyle: {
flex: 0,
width: 340,
height: 270,
resizeMode: 'contain',
marginBottom: 150,
position: 'absolute',
bottom: 50
},
historybuttonStyle: {
width: 38,
height: 35,
position: 'absolute',
right: 5,
top: 10
},
landingbnt1Style: {
width: 250,
height: 45,
top: 175
},
landingbnt2Style: {
width: 250,
height: 45,
top: 200
}
};
export default MainScreen;
this is the error pop up :
SO how can i get this functionality ?
there are several rules you have to follow with react-native:
Images cannot be conditionally require:
<Image
source={require(this.state.computerPressed ? './Images/landing-bnt1-on.png' : './Images/landing-bnt1.png')}
style={styles.landingbnt1Style} />
this statement is not valid. that's why you got your first error message Unknown named module: '../images/landing-btn1-on.png'.
instead, load image statically:
const image_btn1 = require('./Images/landing-btn1.png');
const image_btn1_on = require('./Images/landing-btn1-on.png');
and conditionally switching between them.
<Image
source={this.state.computerPressed ? image_btn1_on : image_btn1}
style={styles.landingbnt1Style} />
JSX Function should be valid:
JSX function won't work with this pattern.
onPressIn={this.state.computerPressed = true}
onPressOut={this.state.computerPressed}>
do this instead:
onPressIn={() => { this.state.computerPressed = true }}
onPressOut={ () => { this.state.computerPressed = false }}>
And,
You should always use setState() outside of constructor:
or render() won't get triggered.
so previous expression becomes:
onPressIn={() => {
this.setState({computerPressed: true });
}
onPressOut={ () => {
this.setState({computerPressed: false });
}
It looks like you have an issue or a conflict in your node_modules.
Try to delete node_modules folder and then run in your terminal
nom install
I have solved this problem hope it helps for others
I have changed from TouchableHighlight to TouchableWithoutFeedback
and made few changes as suggested as answers thank you #Val
So here's my code:
import React, { Component } from 'react';
import { StatusBar, Image, TouchableWithoutFeedback } from 'react-native';
import { Actions } from 'react-native-router-flux';
const a = require('./Images/landing-bnt1-on.png');
const b = require('./Images/landing-bnt1.png');
const c = require('./Images/landing-bnt2-on.png');
const d = require('./Images/landing-bnt2.png');
class MainScreen extends Component {
state = {
computerPressed: false,
teamPressed: false
}
render() {
return (
<Image
source={require('./Images/bg_img.png')}
style={styles.backgroundStyle} >
<StatusBar hidden />
<Image
source={require('./Images/History.png')}
style={styles.historybuttonStyle} />
<Image
source={require('./Images/logo_ws.png')}
style={styles.logoStyle} />
<TouchableWithoutFeedback
onPress={() => {
Actions.screen2({ challenge: 'Computer' });
}
}
onPressIn={() => {
this.setState({ computerPressed: true });
}
}
onPressOut={() => {
this.setState({ computerPressed: false });
}
} >
<Image
source={this.state.computerPressed ? a : b}
style={styles.landingbnt1Style} />
</TouchableWithoutFeedback>
<TouchableWithoutFeedback
onPress={() => {
Actions.screen2({ challenge: 'Team' });
}
}
onPressIn={() => {
this.setState({ teamPressed: true });
}
}
onPressOut={() => {
this.setState({ teamPressed: false });
}
} >
<Image
source={this.state.teamPressed ? c : d}
style={styles.landingbnt2Style} />
</TouchableWithoutFeedback>
</Image>
);
}
}
const styles = {
backgroundStyle: {
flex: 1,
width: undefined,
height: undefined,
justifyContent: 'center',
alignItems: 'center',
flexWrap: 'wrap',
position: 'relative'
},
logoStyle: {
flex: 0,
width: 340,
height: 270,
resizeMode: 'contain',
marginBottom: 150,
position: 'absolute',
bottom: 50
},
historybuttonStyle: {
width: 38,
height: 35,
position: 'absolute',
right: 5,
top: 10
},
landingbnt1Style: {
width: 250,
height: 45,
top: 175
},
landingbnt2Style: {
width: 250,
height: 45,
top: 200
}
};
export default MainScreen;
With version 0.63 React Native introduced a new core component Pressable solving this problem:
import { Pressable, Text, View } from 'react-native';
<Pressable onPress={() => { alert("doSomething") }}>
{({ pressed }) => (
<View style={{ flexDirection: 'row' }}>
<Text> Toggle Image </Text>
{pressed
? <Image source={ require('../assets/img-pressed.png') } />
: <Image source={ require('../assets/img.png') } />}
</View>
)}
</Pressable>
You can also adjust the styles of the wrapper element by doing this:
<Pressable onPress={() => { alert("doSomething") }}
style={({ pressed }) => [
{
backgroundColor : pressed
? 'red'
: 'green'
}
]}>
{({ pressed }) => (
<View style={styles.myCustomStyleWithoutBackgroundColor}>
<Text> Toogle Image and Background Color </Text>
{pressed
? <Image source={ require('../assets/img-pressed.png') } />
: <Image source={ require('../assets/img.png') } />}
</View>
)}
</Pressable>

Categories

Resources