Created custom tab bar

This commit is contained in:
Arnaud Vergnet 2020-04-17 11:57:12 +02:00
parent 26cf3cf2c3
commit 0385e8ad76
6 changed files with 282 additions and 21 deletions

View file

@ -24,7 +24,6 @@
"@react-native-community/masked-view": "0.1.6", "@react-native-community/masked-view": "0.1.6",
"@react-navigation/bottom-tabs": "^5.1.1", "@react-navigation/bottom-tabs": "^5.1.1",
"@react-navigation/drawer": "^5.1.1", "@react-navigation/drawer": "^5.1.1",
"@react-navigation/material-bottom-tabs": "^5.1.1",
"@react-navigation/native": "^5.0.9", "@react-navigation/native": "^5.0.9",
"@react-navigation/stack": "^5.1.1", "@react-navigation/stack": "^5.1.1",
"expo": "^37.0.0", "expo": "^37.0.0",

View file

@ -192,7 +192,7 @@ class SideBar extends React.Component<Props, State> {
try { try {
const state = event.data.state.routes[0].state; // Get the Drawer's state if it exists const state = event.data.state.routes[0].state; // Get the Drawer's state if it exists
// Get the current route name. This will only show Drawer routes. // Get the current route name. This will only show Drawer routes.
// Tab routes will be shown as 'Main' // Tabbar routes will be shown as 'Main'
const routeName = state.routeNames[state.index]; const routeName = state.routeNames[state.index];
if (this.state.activeRoute !== routeName) if (this.state.activeRoute !== routeName)
this.setState({activeRoute: routeName}); this.setState({activeRoute: routeName});

View file

@ -0,0 +1,81 @@
import * as React from 'react';
import {View} from "react-native";
import {withTheme} from 'react-native-paper';
import TabIcon from "./TabIcon";
import TabHomeIcon from "./TabHomeIcon";
type Props = {
state: Object,
descriptors: Object,
navigation: Object,
theme: Object,
}
const TAB_BAR_HEIGHT = 48;
/**
* Abstraction layer for Agenda component, using custom configuration
*/
class CustomTabBar extends React.Component<Props> {
render() {
const state = this.props.state;
const descriptors = this.props.descriptors;
const navigation = this.props.navigation;
return (
<View style={{
flexDirection: 'row',
height: TAB_BAR_HEIGHT,
}}>
{state.routes.map((route, index) => {
const {options} = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
const color = isFocused ? options.activeColor : options.inactiveColor;
const iconData = {focused: isFocused, color: color};
if (route.name !== "home") {
return <TabIcon
onPress={onPress}
onLongPress={onLongPress}
icon={options.tabBarIcon(iconData)}
color={color}
label={label}
focused={isFocused}/>
} else
return <TabHomeIcon
onPress={onPress}
onLongPress={onLongPress}
focused={isFocused}/>
})}
</View>
);
}
}
export default withTheme(CustomTabBar);

View file

@ -0,0 +1,79 @@
// @flow
import * as React from 'react';
import {View} from "react-native";
import {FAB, withTheme} from 'react-native-paper';
import * as Animatable from "react-native-animatable";
type Props = {
focused: boolean,
onPress: Function,
onLongPress: Function,
theme: Object,
}
const AnimatedFAB = Animatable.createAnimatableComponent(FAB);
/**
* Abstraction layer for Agenda component, using custom configuration
*/
class TabHomeIcon extends React.Component<Props> {
focusedIcon = require('../../../assets/tab-icon.png');
unFocusedIcon = require('../../../assets/tab-icon-outline.png');
constructor(props) {
super(props);
Animatable.initializeRegistryWithDefinitions({
fabFocusIn: {
"0": {
scale: 1, translateY: 0
},
"0.9": {
scale: 1.4, translateY: -6
},
"1": {
scale: 1.3, translateY: -5
},
},
fabFocusOut: {
"0": {
scale: 1.3, translateY: -5
},
"1": {
scale: 1, translateY: 0
},
}
});
}
shouldComponentUpdate(nextProps: Props): boolean {
return (nextProps.focused !== this.props.focused);
}
render(): React$Node {
const props = this.props;
return (
<View style={{
flex: 1,
justifyContent: 'center',
}}>
<AnimatedFAB
duration={200}
easing={"ease-out"}
animation={props.focused ? "fabFocusIn" : "fabFocusOut"}
useNativeDriver
onPress={props.onPress}
onLongPress={props.onLongPress}
icon={this.focusedIcon}
style={{
marginLeft: 'auto',
marginRight: 'auto'
}}/>
</View>
);
}
}
export default withTheme(TabHomeIcon);

View file

@ -0,0 +1,111 @@
// @flow
import * as React from 'react';
import {View} from "react-native";
import {TouchableRipple, withTheme} from 'react-native-paper';
import {MaterialCommunityIcons} from "@expo/vector-icons";
import * as Animatable from "react-native-animatable";
type Props = {
focused: boolean,
color: string,
label: string,
icon: string,
onPress: Function,
onLongPress: Function,
theme: Object,
}
const AnimatedIcon = Animatable.createAnimatableComponent(MaterialCommunityIcons);
/**
* Abstraction layer for Agenda component, using custom configuration
*/
class TabIcon extends React.Component<Props> {
firstRender: boolean;
constructor(props) {
super(props);
Animatable.initializeRegistryWithDefinitions({
focusIn: {
"0": {
scale: 1, translateY: 0
},
"0.9": {
scale: 1.6, translateY: 6
},
"1": {
scale: 1.5, translateY: 5
},
},
focusOut: {
"0": {
scale: 1.5, translateY: 5
},
"1": {
scale: 1, translateY: 0
},
}
});
this.firstRender = true;
}
componentDidMount() {
this.firstRender = false;
}
shouldComponentUpdate(nextProps: Props): boolean {
return (nextProps.focused !== this.props.focused)
|| (nextProps.theme.dark !== this.props.theme.dark);
}
render(): React$Node {
const props = this.props;
return (
<TouchableRipple
onPress={props.onPress}
onLongPress={props.onLongPress}
style={{
flex: 1,
justifyContent: 'center',
alignItem: 'center',
}}
>
<View>
<Animatable.View
duration={200}
easing={"ease-out"}
animation={props.focused ? "focusIn" : "focusOut"}
useNativeDriver
>
<AnimatedIcon
name={props.icon}
color={props.color}
size={26}
style={{
marginLeft: 'auto',
marginRight: 'auto',
}}
/>
</Animatable.View>
<Animatable.Text
animation={props.focused ? "fadeOutDown" : "fadeIn"}
useNativeDriver
style={{
color: props.color,
marginLeft: 'auto',
marginRight: 'auto',
}}
>
{props.label}
</Animatable.Text>
</View>
</TouchableRipple>
);
}
}
export default withTheme(TabIcon);

View file

@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import {createStackNavigator, TransitionPresets} from '@react-navigation/stack'; import {createStackNavigator, TransitionPresets} from '@react-navigation/stack';
import {createMaterialBottomTabNavigator} from "@react-navigation/material-bottom-tabs"; import {createBottomTabNavigator} from "@react-navigation/bottom-tabs";
import HomeScreen from '../screens/HomeScreen'; import HomeScreen from '../screens/HomeScreen';
import PlanningScreen from '../screens/Planning/PlanningScreen'; import PlanningScreen from '../screens/Planning/PlanningScreen';
@ -11,9 +11,8 @@ import ProximoMainScreen from '../screens/Proximo/ProximoMainScreen';
import ProximoListScreen from "../screens/Proximo/ProximoListScreen"; import ProximoListScreen from "../screens/Proximo/ProximoListScreen";
import ProximoAboutScreen from "../screens/Proximo/ProximoAboutScreen"; import ProximoAboutScreen from "../screens/Proximo/ProximoAboutScreen";
import PlanexScreen from '../screens/Websites/PlanexScreen'; import PlanexScreen from '../screens/Websites/PlanexScreen';
import {MaterialCommunityIcons} from "@expo/vector-icons";
import AsyncStorageManager from "../managers/AsyncStorageManager"; import AsyncStorageManager from "../managers/AsyncStorageManager";
import {FAB, useTheme, withTheme} from 'react-native-paper'; import {useTheme, withTheme} from 'react-native-paper';
import i18n from "i18n-js"; import i18n from "i18n-js";
import ClubDisplayScreen from "../screens/Amicale/Clubs/ClubDisplayScreen"; import ClubDisplayScreen from "../screens/Amicale/Clubs/ClubDisplayScreen";
import ScannerScreen from "../screens/ScannerScreen"; import ScannerScreen from "../screens/ScannerScreen";
@ -21,6 +20,7 @@ import MaterialHeaderButtons, {Item} from "../components/Custom/HeaderButton";
import FeedItemScreen from "../screens/FeedItemScreen"; import FeedItemScreen from "../screens/FeedItemScreen";
import {createCollapsibleStack} from "react-navigation-collapsible"; import {createCollapsibleStack} from "react-navigation-collapsible";
import GroupSelectionScreen from "../screens/GroupSelectionScreen"; import GroupSelectionScreen from "../screens/GroupSelectionScreen";
import CustomTabBar from "../components/Tabbar/CustomTabBar";
const TAB_ICONS = { const TAB_ICONS = {
home: 'triangle', home: 'triangle',
@ -306,7 +306,7 @@ function PlanexStackComponent() {
); );
} }
const Tab = createMaterialBottomTabNavigator(); const Tab = createBottomTabNavigator();
type Props = { type Props = {
defaultRoute: string | null, defaultRoute: string | null,
@ -329,16 +329,6 @@ class TabNavigator extends React.Component<Props> {
this.createHomeStackComponent = () => HomeStackComponent(props.defaultRoute, props.defaultData); this.createHomeStackComponent = () => HomeStackComponent(props.defaultRoute, props.defaultData);
} }
getHomeButton(focused: boolean) {
let icon = focused ? require('../../assets/tab-icon.png') : require('../../assets/tab-icon-outline.png')
return (
<FAB
icon={icon}
small
style={{position: 'absolute', top: '-25%'}}/>
);
}
render() { render() {
return ( return (
<Tab.Navigator <Tab.Navigator
@ -349,14 +339,15 @@ class TabNavigator extends React.Component<Props> {
let icon = TAB_ICONS[route.name]; let icon = TAB_ICONS[route.name];
icon = focused ? icon : icon + ('-outline'); icon = focused ? icon : icon + ('-outline');
if (route.name !== "home") if (route.name !== "home")
return <MaterialCommunityIcons name={icon} color={color} size={26}/>; return icon;
else else
return this.getHomeButton(focused); return null;
}, },
tabBarLabel: route.name !== 'home' ? undefined : '' tabBarLabel: route.name !== 'home' ? undefined : '',
activeColor: this.props.theme.colors.primary,
inactiveColor: this.props.theme.colors.tabIcon
})} })}
activeColor={this.props.theme.colors.primary} tabBar={props => <CustomTabBar {...props} />}
inactiveColor={this.props.theme.colors.tabIcon}
> >
<Tab.Screen <Tab.Screen
name="proximo" name="proximo"