Ripple effect on TabBarBottom - react-native

How to add ripple animation when a tabs is clicked in TabBarBottom. I want ripple effect just like Youtube app

You can use Screen's Navigation Options to add desired styled tabBarIcon
tabBarIcon
React Element or a function that given { focused: boolean, tintColor:
string } returns a React.Element, to display in tab bar
tabBarLabel
Title string of a tab displayed in the tab bar or React Element or a
function that given { focused: boolean, tintColor: string } returns
a React.Element, to display in tab bar. When undefined, scene title
is used. To hide, see tabBarOptions.showLabel in the previous
section.
tabBarOnPress
Callback to handle tap events; arguments are the scene: { route,
index } that was tapped and a jumpToIndex method that can perform
the navigation for you.
Example
const MyApp = TabNavigator({
Home: {
screen: MyHomeScreen,
navigationOptions: ({navigation}) => ({
tabBarIcon: ({focuced, tintColor}) => (
<MyCustomIcon focuced={focuced} tintColor={tintColor} name="Home" />
)
})
},
Notifications: {
screen: MyNotificationsScreen,
navigationOptions: ({navigation}) => ({
tabBarIcon: ({focuced, tintColor}) => (
<MyCustomIcon focuced={focuced} tintColor={tintColor} name="Notifications" />
)
})
},
});

Related

Is there a option to disable tabbar buttons

In my react native app, I have a router component which uses react-navigation-material-bottom-tabs.
In that component I have created it like this.
const tabNavigator = createMaterialBottomTabNavigator({
home: {
screen: Home,
navigationOptions: ({ navigation }) => ({
title: ''
})
},
createCampaign: {
screen: CreateCampaign,
navigationOptions: ({ navigation }) => ({
title: '',
tabBarVisible: false
})
},
settings: {
screen: AllSettings,
navigationOptions: ({ navigation }) => ({
title: ''
})
}
});
This is working fine. But I want to disable some tabs of this bar under some conditions. As example, if the profile hasn't been approved, disable the tab to settings. Is there any way to do this in my screens?(Better if it's not in the router because I can't send a API request in router). How can I access tabBar options in screens? How to disable tabs? Please help.
For Version 5.x there's a new way to do it.
<Tabs.Screen
name="Chat"
component={Chat}
listeners={{
tabPress: e => {
// Prevent default action
e.preventDefault();
},
}}
/>
Here's the reference link to the docs: https://reactnavigation.org/docs/navigation-events/
What are you using for global state management inside your app, please store the status weather profile is approved or not inside your global state. Then you can override tabBarOnPress to check if user is approved and perform the actions accordingly, code snippet below.
const Tab_Navigator = createBottomTabNavigator({
First:{
screen: First,
},
Second:{
screen: Second,
},
Third:{
screen: Third,
}
}, defaultNavigationOptions: ({ navigation }) => ({
tabBarOnPress: ({ navigation, defaultHandler }) => {
if (
navigation.state.routeName === "Second" ||
navigation.state.routeName === "Third"
) {
return null;
}
defaultHandler();
},})
You can try with tabBarComponent props available in the second parameter for createBottomTabNavigator.
You can enable or disable button as you like but please look at the note below.
I used native base Footer, you can use your preferred components.
import { Footer, FooterTab } from 'native-base'
const HomeScreen = createBottomTabNavigator(
{
First:{
screen: First,
},
Second:{
screen: Second,
},
Third:{
screen: Third,
}
},
{
tabBarComponent: props => {
return (
<Footer>
<FooterTab>
<Button
vertical
active={props.navigation.state.index === 0}
onPress={() => props.navigation.navigate(actions.First)}
>
</Button>
<Button
vertical
active={props.navigation.state.index === 1}
onPress={() => props.navigation.navigate(actions.Second)}
>
</Button>
<Button
vertical
active={props.navigation.state.index === 2}
onPress={() => props.navigation.navigate(actions.Third)}
>
</Button>
</FooterTab>
</Footer>
)
}
}
)
Note: If you want to dynamically change your buttons(tab bars elements) you can not do it as these buttons are assigned on the basis of the index.
I am Working on the old Project there used React-Navigation version 4.
The Solution that worked for me is follow ....
Home: {
screen: HomeStack,
navigationOptions: {
tabBarOnPress:()=>{
return null;
},
tabBarLabel: "Home",
tabBarOptions: {
activeTintColor: "#455BE0",
inactiveTintColor: "gray",
},
tabBarIcon: ({ tintColor }) => (
<Image
style={{ height: 25, width: 25, tintColor: tintColor, marginTop: 10 }}
source={require("./src/assets/home.png")}
/>
),
},
},
You can check in the navigationOptions added tabBarOnPress which return value is null.
This solution work for me Hope work for you also.
You can overwrite tabBarButton​ using Pressable or Button component and add a conditional in order to disable the button. Something like this :
<Stack.Screen
name="YourScreen"
options={{
tabBarButton: (props) => (
<Pressable
{...props}
isDisabled={conditional ? true : false}
/>
),
}}
component={YourComponent}
/>

React Native Navigation To External Tab Within BottomTabBar Screen

I have an issue where I do not know how to navigate from a screen in a bottom tab bar that looks like this:
default: createBottomTabNavigator(
{
Home: {
screen: HomeScreen,
navigationOptions: {
tabBarIcon: ({ tintColor }) => <Ionicons name="ios-home" size={24} color={tintColor} />
}
},
Message: {
screen: MessageScreen,
navigationOptions: {
tabBarIcon: ({ tintColor }) => <Ionicons name="ios-chatboxes" size={24} color={tintColor} />,
OtherUser: { screen: OtherUserScreen }
}
},
Post: {
screen: PostScreen,
navigationOptions: {
tabBarIcon: ({ tintColor }) => <Ionicons name="ios-add-circle" size={48} color={"#4cdd75"} style={{
shadowColor: "#4cdd75",
shadowOffset: {
width: 0,
height: 0
},
shadowRadius: 10,
shadowOpacity: 0.3
}} />
}
},
Notification: {
screen: NotificationScreen,
navigationOptions: {
tabBarIcon: ({ tintColor }) => <Ionicons name="ios-notifications" size={24} color={tintColor} />
}
},
Profile: {
screen: ProfileScreen,
navigationOptions: {
tabBarIcon: ({ tintColor }) => <Ionicons name="ios-person" size={24} color={tintColor} />
}
}
}
With the createSwitchNavigator component looking like this:
export default createAppContainer(
createSwitchNavigator(
{
Loading: LoadingScreen,
App: AppContainer,
Auth: AuthStack,
OtherUser: OtherUserScreen
},
{
initialRouteName: 'Loading'
}
)
);
The AppContainer is the bottom tab navigator screen setup.
Additionally, My navigation from within my message screen looks like this:
const { navigate } = this.props.navigation;
...
onPress={() => navigate('OtherUser')}
From this message screen I want to navigate to the OtherUser screen so that the bottom tab navigator is still shown. Currently, the navigation navigates to the OtherUser screen, but the bottom tab navigator dissapears. And when I try to use the back button code navigation in my OtherUser Screen that looks like this:
onPress={() => navigate("MessageScreen")}
Nothing is shown. Would it be possible in any way to have the navigation from message screen to the other user screen seemless without deleting the bottom tab bar and without adding another component to it?
From what I see, what you're trying to do is wrong.
It makes sense that the bottomBar disappears because you use a SwitchNavigator to navigate between "AppContainer" and "OtherUser".
So the moment you navigate to "OtherUser", you are no longer in a bottomMenu navigation, you are simply in a SwitchNavigator!
To be able to do what you want to do, you should integrate a stackNavigator instead of MessageScreen,
then in this StackNavigator, you integrate your MessageScreen as well as OtherUser
Currently, your navigation seems to be like this:
- Loading
- App
-- bottomTabMenu
-- Home
-- Message
-- Posts
-- Notifications
-- Profile
- Auth
- OtherUser
So as you see, when you go to "OtherUser" you are not in a BottomMenu navigation anymore, besides that, you can't go back because actually, to be able to go back with a back button, you need to be in a stack navigation.
So if you want to be able to go to the user profile from your messageScreen, you need to wrap it in a navigation stack, and integrate this stack into your bottomMenu.
Your navigation should then look something like this:
- Loading
- App
-- bottomTabMenu
-- Home
-- Message
-- Stack Navigation
-- Message Screen (defaultRoute)
-- OtherUser Screen
-- Posts
-- Notifications
-- Profile
- Auth
So your code will be something like this:
const MessageStack = createStackNavigator(
{
Message: MessageScreen,
OtherUser: OtherUserScreen
},
{
initialRouteName: "Message"
}
)
default: createBottomTabNavigator(
{
...
Message: {
screen: MessageStack,
navigationOptions: {
tabBarIcon: ({ tintColor }) => <Ionicons name="ios-chatboxes" size={24} color={tintColor} />,
OtherUser: { screen: OtherUserScreen } //Delete this line, the navigationOptions are only used to define styles or behaviors on the navigation.
}
},
...
}
I hope I understood the question and that this answer will help you!
Viktor

How can I render a Tab Item for React Navigation Material Bottom Tab based on store value?

I'm using React Navigation Material Bottom Tabs and I need to add an option based on a store value.
I have a Material Bottom Tab for Authenticated user, then I wanna get the user kind value from store and render a tab item based on value at my reducer.
I tried the below solution but I always get store initial state.
createMaterialBottomTabNavigator(
{
Events: {
screen: EventsStack,
navigationOptions: {
tabBarLabel: 'Agenda',
tabBarIcon: ({ tintColor }) => (
<MaterialCommunityIcons name="calendar" size={ICON_SIZE} color={tintColor} />
),
},
},
...((() => {
const userKind = store.getState() && store.getState().auth.user.kind;
return userKind === 'p';
})() && {
Consultations: {
screen: PatientsStack,
navigationOptions: {
tabBarLabel: 'Atendimentos',
tabBarIcon: ({ tintColor }) => (
<MaterialCommunityIcons name="doctor" size={ICON_SIZE} color={tintColor} />
),
},
},
}),
Patients: {
screen: PatientsStack,
navigationOptions: {
tabBarLabel: 'Pacientes',
tabBarIcon: ({ tintColor }) => (
<MaterialCommunityIcons name="account-multiple" size={ICON_SIZE} color={tintColor} />
),
},
},
Settings: {
screen: SettingsStack,
navigationOptions: {
tabBarLabel: 'Ajustes',
tabBarIcon: ({ tintColor }) => (
<MaterialCommunityIcons name="settings" size={ICON_SIZE} color={tintColor} />
),
},
},
},
{
initialRouteName: 'Events',
labeled: false,
shifting: false,
activeTintColor: Colors.dsBlue,
barStyle: {
borderTopWidth: 1,
borderTopColor: Colors.dsSky,
backgroundColor: Colors.colorWhite,
},
tabBarOptions: {
activeTintColor: Colors.dsBlue,
activeBackgroundColor: Colors.dsSkyLight,
inactiveTintColor: Colors.dsInkLighter,
inactiveBackgroundColor: Colors.dsSkyLight,
upperCaseLabel: false,
labelStyle: {
fontSize: 11,
},
style: {
backgroundColor: Colors.dsSkyLight,
},
showIcon: true,
pressColor: Colors.dsSkyLight,
indicatorStyle: {
backgroundColor: Colors.dsBlue,
},
},
},
)
I want to conditionally render Consultations tab item based on my store value.
Using store.getState() in the navigator will give you the initial store but as you have pointed out, it will not get called again when the store is updated, so the navigator will never change.
The solution to have the navigation updated when the state changes is to use a component that is connected to the Redux store.
Let's say you want to change the title of a tab depending on a value in your Redux store.
In that case, you would simply define a Label.js component like so:
import React from 'react';
import { connect } from 'react-redux';
import { Text } from 'react-native';
const Label = ({ user }) => {
if (!user) return 'Default label';
return <Text>{user.kind}</Text>;
};
function mapStateToProps(state) {
return { user: state.auth.user };
}
export default connect(mapStateToProps)(Label);
Here we assume that you have a reducer that updates the auth key in your store with the user object whenever it changes.
Now all you would have to do is import <Label /> in your navigation:
import Label from './Label';
export default createMaterialBottomTabNavigator({
Events: {
screen: EventsStack,
navigationOptions: {
tabBarLabel: <Label />,
},
},
});
VoilĂ ! Whenever you update auth.user.kind in your store, the tab navigation gets updated.
Now I am aware that in your case you want something a bit more complicated than updating the label depending on the store, you want to display or hide a tab dynamically.
Unfortunately react-navigation does not provide a hideTab option for a given tab yet. There is a tabBarVisible option but it only applies to the whole bar, not a single tab.
With the labels, we managed to do it by connecting a component to the store. But what component should we target here?
The workaround is to use the tabBarComponent option. It allows us to override the component to use as the tab bar. So we just have to override it with a component that is connected to the store and we have our dynamic tab bar!
Now our createMaterialBottomTabNavigator becomes:
import WrappingTabBar from './WrappingTabBar';
export default createMaterialBottomTabNavigator({
Events: {
screen: EventsStack,
navigationOptions: {
tabBarLabel: 'Agenda',
},
},
Consultations: {
screen: PatientsStack,
navigationOptions: {
tabBarLabel: 'Atendimentos',
},
},
}, {
tabBarComponent: props => <WrappingTabBar {...props} />, // Here we override the tab bar component
});
And we define <WrappingTabBar> in WrappingTabBar.js by rendering a basic BottomTabBar connected to the store and that filters out the routes in the navigation state that we do not want.
import React from 'react';
import { connect } from 'react-redux';
import { BottomTabBar } from 'react-navigation';
const TabBarComponent = props => <BottomTabBar {...props} />; // Default bottom tab bar
const WrappingTabBar = ({ navigation, user, ...otherProps }) => (
<TabBarComponent
{...otherProps}
navigation={{
...navigation,
state: {
...navigation.state,
routes: navigation.state.routes.filter(r => r.routeName !== 'Consultations' || (user && user.kind === 'p')), // We remove unwanted routes
},
}}
/>
);
function mapStateToProps(state) {
return { user: state.auth.user };
}
export default connect(mapStateToProps)(WrappingTabBar);
This time, whenever your auth reducer receives a new value for auth.user, it updates the store. Since WrappingTabBar is connected to the store, it renders again with a new value of auth.user.kind. If the value is "p", the route corresponding to the second screen gets removed from the navigation state and the tab gets hidden.
This is it! We end up with a tab navigation that can display tabs dynamically depending on values in our Redux store.

How to hide bottom tab navigator when scroll up the screen?

Is there a way to hide the bottom tab navigator when you scroll up the screen?
I have attempted to use route params to set tabBarVisible to false but I think it is not working because I have nested navigator. In other words, my tab in my tab navigator is another stack navigator.
In my bottom tab navigator I tried doing this...
Tab1: {
screen: OtherStack,
navigationOptions: ({navigation}) => {
const routeParams = navigation.state.params
return {
tabBarVisible: routeParams && routeParams.tabBarVisible,
tabBarIcon: ...
}
}
},
},
Then in my OtherStack I use createNavigator to create a screen like this...
NewScreen: {
screen: NewScreen,
navigationOptions: ({ navigation }) => {
return {
headerTitle: <Header />,
headerStyle: {backgroundColor: '#f6f6f6'},
headerLeft: null,
}
}
Then as a test, in the NewScreen I am calling a function via touchableOpacity onPress...
onPress={() => navigation.setParams({ tabBarVisible: false })}
But nothing happens. I want to get this to hide the tab bar when I click the button, and after I get that to work I want to call that function onScroll.
Does anyone know how to do this?

Tab Navigator On focussed

I have a TabNavigator
const Tabs = TabNavigator({
Test: {
screen: SCA,
navigationOptions: () => ({
title: 'Home',
tabBarIcon: ({ tintColor }) => {
return (
<FAIcon
name='screen1'
size={25}
color={tintColor}
/>
);
}
})
},
Screen2: {
screen: SCB,
navigationOptions: () => ({
title: 'screen2',
tabBarIcon: ({ tintColor }) => {
return (
<MIcon
name='account-circle'
size={25}
color={tintColor}
/>
);
}
})
},
screen3: {
screen: MYSCREEN,
navigationOptions: () => ({
title: 'dd',
tabBarIcon: ({ tintColor }) => {
return (
<MIcon
name='account-circle'
size={25}
color={tintColor}
/>
);
}
})
}
}, {
tabBarPosition: 'top',
tabBarOptions: {
showIcon: true,
showLabel: true,
inactiveTintColor: Colors.blue,
activeTintColor: Colors.redColor,
pressColor: Colors.redColor,
indicatorStyle: { backgroundColor: Colors.redColor },
style: {
backgroundColor: Colors.white
}
}
});
Basically I have tab navigator with 3 tabs. Whenever screen3 is focussed via tab button press or by swiping how can I get to know in MYSCREEN that this screen is focussed again.(Screen looks similar to playstore app with selecting screen on tab as well as swiping)
Class MYSCREEN extends React.Component{
//some function like on onfocus?
}
I tried searching it showed onFocusComponent will work but it didn't.
What should I do?
Since you are using react-navigation you can use listeners on the navigation lifecycle events. https://reactnavigation.org/docs/en/navigation-prop.html#addlistener-subscribe-to-updates-to-navigation-lifecycle
There are four events you can subscribe to:
willFocus - the screen will focus
didFocus - the screen focused (if there was a transition, the transition completed)
willBlur - the screen will be unfocused
didBlur - the screen unfocused (if there was a transition, the transition completed)
You can subscribe to as many of them as you want. Here is an example of using didFocus. You could easily replicate this for all that you require.
So in each of the screens of your TabNavigator, you can add the following:
componentDidMount () {
// add listener
this.didFocusSubscription = this.props.navigation.addListener('didFocus', this.didFocusAction);
}
componentWillUmount () {
// remove listener
this.didFocusSubscription.remove();
}
didFocusAction = () => {
// do things here when the screen focuses
}
You can use withNavigationFocus HOC which will add isFocused prop to your screen component's props then catch that prop in getDerivedStateFromProps method and check if isFocused is trueand then return a new state so that the component will render again.