React Native: Switch from createBottomTabNavigator to createMaterialBottomNavigator - android

I am fully aware that using createBottomTabNavigator will give you a nice iOS bottom tabs with no background color while createMaterialBottomNavigator will give you a background color and different setup on android device.
My goal is to to use createBottomTabNavigator on iOs device and then switch to createMaterialBottomNavigator on android device with all the setup like putting activeTintColor color, using shifting: true mode whenever you switch screen on android.
So what I did is I use the Platform API:
const MealsFavTabNavigator =
Platform.OS === 'android'
? createMaterialBottomNavigator(navTabConfig)
: createBottomTabNavigator();
And then, create a possible config:
const navTabConfig = {
options={{
tabBarIcon: ({ focused, color, size }) => (
<Ionicons name="ios-star" size={25} color={focused ? "tomato" : "black"} />
)
}}
};
Then tried to switch this on my navigator:
<MealsFavTabNavigator.Screen
name="Meals"
component={MealsNavigator}
navTabConfig
/>
Which did not work. Here's the full code:
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { Ionicons } from '#expo/vector-icons';
import { Platform } from 'react-native';
import { createMaterialBottomTabNavigator } from '#react-navigation/material-bottom-tabs';
import CategoriesScreen from '../screens/CategoriesScreen';
import CategoryMealsScreen from '../screens/CategoryMealsScreen';
import MealDetailScreen from '../screens/MealDetailScreen';
import FavoritesScreen from '../screens/FavoritesScreen';
import HeaderButton from '../components/HeaderButton';
import { HeaderButtons, Item } from 'react-navigation-header-buttons';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { CATEGORIES } from '../data/dummy-data';
import Colors from '../constants/colors';
const MealsNav = createStackNavigator();
const MealsNavigator = () => {
return (
<MealsNav.Navigator
mode="modal"
screenOptions={{
headerStyle: {
backgroundColor: Colors.primaryColor,
},
headerTintColor: '#fff',
headerTitleStyle: {
fontSize: 17
}
}}
>
<MealsNav.Screen
name="Categories"
component={CategoriesScreen}
options={{
title: 'Meals Categories'
}}
/>
<MealsNav.Screen
name="CategoryMeals"
component={CategoryMealsScreen}
options={({ route }) => {
const catId = route.params.categoryId;
const selectedCategory = CATEGORIES.find((cat) => cat.id === catId);
return {
title: selectedCategory.title,
};
}}
/>
<MealsNav.Screen
name="MealDetail"
component={MealDetailScreen}
options={{
title: 'Meal Detail',
headerRight: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Favorite'
iconName='ios-star'
onPress={() => console.log('Mark as the favorite')}
/>
</HeaderButtons>
),
}}
/>
</MealsNav.Navigator>
);
};
const MealsFavTabNavigator =
Platform.OS === 'android'
? createMaterialBottomNavigator(navTabConfig)
: createBottomTabNavigator();
const navTabConfig = {
options={{
tabBarIcon: ({ focused, color, size }) => (
<Ionicons name="ios-star" size={25} color={focused ? "tomato" : "black"} />
)
}}
};
const MealsTabNav = () => {
return (
<NavigationContainer>
<MealsFavTabNavigator.Navigator
tabBarOptions={{
activeTintColor: 'tomato',
inactiveTintColor: 'black',
}}>
<MealsFavTabNavigator.Screen
name="Meals"
component={MealsNavigator}
navTabConfig
/>
<MealsFavTabNavigator.Screen
name="Favorites"
component={FavoritesScreen}
options={{
tabBarIcon: ({ focused, color, size }) => (
<Ionicons name="ios-star" size={25} color={focused ? "tomato" : "black"} />
)
}}
/>
</MealsFavTabNavigator.Navigator>
</NavigationContainer>
);
};
export default MealsTabNav;
How can I attain my goal of switching from createBottomTabNavigator to createMaterialBottomNavigator and then applying the basic styles on createMaterialBottomNavigator such as activeTintcolor and shifting mode.
Please help! I am super stuck!

Your approach is correct only little change that you need to do is the way you use the props.
The below code is a generic example, I'm not using your components
The tab creation is same but the when you get the props its better if you can use a function like below.
Here the function will return the props for the appropriate navigator and we use the spread operator to pass the props.
You can chose to do this only tabBarOptions but this example shows for all props which is more flexible.
const Tab = Platform.OS === 'ios' ? createMaterialBottomTabNavigator() : createBottomTabNavigator();
const getNavigationOptions = () => {
if (Platform.OS === 'ios') {
//Props for the ios navigator
return {
labeled: false,
initialRouteName: 'Settings',
activeColor: 'red',
inactiveColor: 'white'
};
}
//Props for any other OS navigator
return {
initialRouteName: 'Home',
tabBarOptions: { activeTintColor: 'red' },
};
};
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator {...getNavigationOptions()}>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
To apply this to your project
//Keep this line as it is
const MealsFavTabNavigator =
Platform.OS === 'android'
? createMaterialBottomNavigator()
: createBottomTabNavigator();
<MealsFavTabNavigator.Navigator {...getNavigationOptions()}>
And update the getNavigationOptions with your own probs based on the platform

Did you try the following?
import { createMaterialBottomTabNavigator } from '#react-navigation/material-bottom-tabs';
const Tab = createMaterialBottomTabNavigator();
function MyTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
);
}

Related

how to display a fetch of react-redux on the Drawer.Screen header

I would like to display data on a Status header bar of Drawer Screen.
My code:
import React, {useEffect, useRef, useState} from 'react';
import {Dimensions, FlatList, Text} from 'react-native';
import {useDispatch, useSelector} from 'react-redux';
import AsyncStorage from '#react-native-async-storage/async-storage';
import {get_agency_direction} from '../store/Log/Dir/DirActions';
import Direction from '../screens/Direction/Direction';
import {createDrawerNavigator} from '#react-navigation/drawer';
const Drawer = createDrawerNavigator();
const RDirection = () => {`enter code here`
const info_agency = useSelector(state => state.DirReducers.info_agency);
const dispatch = useDispatch();
useEffect(() => {
try {
AsyncStorage.getItem('token').then(async value => {
dispatch(get_agency_direction(value));
});
} catch (e) {
console.log(e);
}
});
console.log(info_agency);
return (
<Drawer.Navigator
initialRouteName="Direction"
screenOptions={{
drawerStyle: {
width: Dimensions.get('window').width - 40,
},
}}
drawerType="permanent"
drawerContent={props => <DrawerDirection {...props} />}
backBehavior="history">
<Drawer.Screen
name="Direction"
component={Direction}
options={({navigation}) => ({
header: () => (
<HeaderUser
Gtitle='Noblesse voyages'
Depart="Douala"
Arrive="Yaoundé"
onPress={() => {
navigation.toggleDrawer();
}}
/>
),
headerStyle: {
height: 70,
},
})}
/>
</Drawer.Navigator>
);
};
export default RDirection;
The return code by console.log(info_agency) :
[{
"id":46,
"NomCh":"Bricks",
"PrenomCh":"Wandem foué",
"MotdepasseCh":"$2b$10$/4ugSQMptSp6VqScLcHl4.V9oyrgnAk7NNmtZIeRZ7usT1hcMsbB2",
"NumeroCh":673845359,
"NomAgence":"Talla voyages",
"NomOrganisation":"Talla",
"idch":49,
"CodesecureAgence":"$2b$10$lJ0oBoWr7vnI0Ix.YpWDo.tsiYJ8bSm/yLhVSDf8kgT4CfYFZm7Bm",
"NumeroSerieAg":"",
}]
I would have a truck like that
header: () => (
<HeaderUser
Gtitle={info_agency.NomAgence}
Depart={/* Not important*/}
Arrive={/* Not important*/}
onPress={() => {
navigation.toggleDrawer();
}}
/>
I tried with a Flatlist, but without success.
Your console.log shows that you're response returns an array. You're trying to call info_agency.NomAgence which won't work since it's not an object.
If you want to pass the title into Gtitle you would need to get the object form the array:
info_agency[0].NomAgence

React native mapbox-gl crashes with react-navigation

I have a problem. When I display a map with #rnmapbox/maps and that I want to navigate in my screens, the app freeze then crashes. I'm pretty sure that this is a rnmapbox problem because when I remove the code between <MapboxGL.MapView> and <MapboxGL.MapView/>, navigation works fine.
I use #react-navigation/native and #react-navigation/native-stack
Note that this issus don't append on iOS, only on Android.
Record of the issue : https://vimeo.com/723749736
Here is my code :
App.js
import React from 'react';
import MapScreen from './src/screens/MapScreen';
import PlaceDetailsScreen from './src/screens/PlaceDetailsScreen';
import VideoPlayerScreen from './src/screens/VideoPlayerScreen';
import {createNativeStackNavigator} from '#react-navigation/native-stack';
import {NavigationContainer} from '#react-navigation/native';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import {GestureHandlerRootView} from 'react-native-gesture-handler';
function App() {
const Stack = createNativeStackNavigator();
return (
<SafeAreaProvider>
<GestureHandlerRootView style={{flex: 1}}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Map"
component={MapScreen}
options={{headerShown: false}}
/>
<Stack.Group>
<Stack.Screen name="Details" component={PlaceDetailsScreen} />
<Stack.Screen
name="VideoPlayer"
component={VideoPlayerScreen}
options={{headerShown: false}}
/>
</Stack.Group>
</Stack.Navigator>
</NavigationContainer>
</GestureHandlerRootView>
</SafeAreaProvider>
);
}
export default App;
MapScreen.js
import React, {useState, useRef, useMemo, useCallback, useEffect} from 'react';
import {View, Text, PermissionsAndroid, Platform} from 'react-native';
import MapboxGL from '#rnmapbox/maps';
import BottomSheet, {BottomSheetScrollView} from '#gorhom/bottom-sheet';
import Styles from '../../Styles';
import PlaceBtn from '../components/PlaceBtn/PlaceBtn';
import IconPin from './../assets/icons/icon-locationPin.svg';
MapboxGL.setAccessToken(
'XXXXXXXXXXXXXXXXXXXXXXXXXXX',
);
function MapScreen({navigation}) {
const [hasGeolocPermission, setHasGeolocPermission] = useState(false);
const [currentRegion, setCurrentRegion] = useState([4.824, 45.76]); //longitude, latitude
const [userLocation, setUserLocation] = useState();
const [nearestPoints, setNearestPoints] = useState([]);
const markers = require('../data/markers.json');
const route = require('../data/route.json');
const routeLine = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: route.points,
},
},
],
};
const bottomSheetRef = useRef(null);
const snapPoints = useMemo(() => ['13%', '75%', '100%'], []);
const handleSheetChanges = useCallback(index => {
console.log('handleSheetChanges', index);
}, []);
const requestLocationPermission = async () => {
if (Platform.OS === 'android') {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('location uses is granted');
setHasGeolocPermission(true);
} else {
console.log(granted);
console.log('location access denied');
}
} catch (error) {
console.warn('error while request location : ', error);
}
}
};
useEffect(() => {
requestLocationPermission();
}, []);
return (
<View style={Styles.container}>
<MapboxGL.MapView style={Styles.map} scaleBarEnabled={false}>
<MapboxGL.UserLocation
animated
showsUserHeadingIndicator
minDisplacement={1}
onUpdate={position => {
console.log('position : ', position);
}}
/>
<MapboxGL.Camera centerCoordinate={currentRegion} zoomLevel={14} />
{markers.map((marker, index) => {
if (marker.category === 'introduction') {
console.log('intro');
} else {
return (
<MapboxGL.PointAnnotation
key={index}
id={marker.id + ''}
coordinate={[marker.longitude, marker.latitude]}>
<IconPin width={35} height={35} fill={'#00c0aa'} />
</MapboxGL.PointAnnotation>
);
}
})}
<MapboxGL.ShapeSource id="line1" shape={routeLine}>
<MapboxGL.LineLayer
id="linelayer1"
style={{lineColor: 'red', lineWidth: 5}}
/>
</MapboxGL.ShapeSource>
</MapboxGL.MapView>
<BottomSheet
ref={bottomSheetRef}
handleIndicatorStyle={Styles.handleIndicator}
index={0}
snapPoints={snapPoints}
onChange={handleSheetChanges}>
<View style={Styles.bottomSheetContent}>
<Text style={Styles.bottomSheetTitle}>{route.name}</Text>
<BottomSheetScrollView>
{markers.map((place, index) => {
return (
<PlaceBtn
place={place}
navigation={navigation}
//isNear={nearestPoints.includes(place.id)}
/>
);
})}
</BottomSheetScrollView>
</View>
</BottomSheet>
</View>
);
}
export default MapScreen;
PlaceDetailsScreen.js
import React from 'react';
import {View, Text, TouchableOpacity} from 'react-native';
function PlaceDetailsScreen({navigation}) {
return (
<>
<TouchableOpacity
style={{backgroundColor: '#00FF00'}}
onPress={() => navigation.navigate('Map')}>
<Text>Go to MAP</Text>
</TouchableOpacity>
<TouchableOpacity
style={{backgroundColor: '#00FF00'}}
onPress={() => navigation.navigate('VideoPlayer')}>
<Text>Go to Video Player</Text>
</TouchableOpacity>
</>
);
}
```
I had the same problem. In my case I have a modal that is persistent through all screens and that modal shrinks when user swipes down like in youtube. After I initialized map inside modal, whenever I try to change screen my app were first freezing then crashing.
My solution was to render map only when modal is expanded. So I made a conditional rendering. Checked whether modal is shrinked or not. If shrinked, then render the map, else don't render. Thus my navigation worked without any problem.
This was my case and that's how I solved it:
// I use reduxjs toolkit.
// Since this modal is gonna be persistent through all screens
// It's best to store translateY value in redux.
const Modal = useAppSelector((state) => state.Modal);
const translateY = useSharedValue(Modal.translateY);
// I watch this variable with an useEffect hook to dynamically update redux value
const [translateYState, setTranslateYState] = useState(translateY.value);
useEffect(() => {
dispatch(setTranslateY(translateYState));
}, [translateYState]);
// translateYState's value is dynamically updating by a pan gesture detector
// translateYState value to be 0 means modal is expanded and user is using the modal
return (
<>
{translateYState==0 && (
<MapboxGL.MapView style={styles.map}></MapboxGL.MapView>
)}
</>
)
You can un-render the map when the modal which has title "Les Sens de Lyon" expands. If you are worried about your map data, you can store the map data for example all locations of markers in redux to be persistent but if your app is a single page app it should not be a problem else you can use redux.

React Native - Navigate to screen - Invalid hook call

App.js:
import React, { Component } from 'react';
import {View,Text} from 'react-native';
import { createDrawerNavigator } from 'react-navigation-drawer';
import {createAppContainer} from 'react-navigation';
import Epics from './screens/tmp';
import Pager from './screens/Pager';
const DrawerNavigator = createDrawerNavigator({
Home: {screen: Epics},
Page: {screen: Pager}
},{initialRouteName: 'Home'});
const Stack = createAppContainer(DrawerNavigator);
export default class App extends Component {
render() {
return <Stack />;
}
}
Trying to navigate to a screen through a custom component:
import { ActivityIndicator, Image, StyleSheet, View, TouchableHighlight } from 'react-native';
import PropTypes from 'prop-types';
import React from 'react';
import { useNavigation } from 'react-navigation-hooks';
import AuthorRow from './AuthorRow';
export default class Card extends React.Component {
static propTypes = {
fullname: PropTypes.string.isRequired,
image: Image.propTypes.source.isRequired,
linkText: PropTypes.string.isRequired,
onPressLinkText: PropTypes.func.isRequired,
epicId: PropTypes.string.isRequired,
};
state = {
loading: true,
};
getNav(){
return useNavigation();
}
handleLoad = () => {
this.setState({ loading: false });
};
render() {
const { fullname, image, linkText, onPressLinkText, epicId, prop } = this.props;
const { loading } = this.state;
return (
<View>
<AuthorRow
fullname={fullname}
linkText={linkText}
onPressLinkText={onPressLinkText}
/>
<TouchableHighlight onPress={() => this.getNav().navigate('Pager')} underlayColor="white">
<View style={styles.image}>
{loading && (
<ActivityIndicator style={StyleSheet.absoluteFill} size={'large'} />
)}
<Image
style={StyleSheet.absoluteFill}
source={image}
onLoad={this.handleLoad}
/>
</View>
</TouchableHighlight>
</View>
);
}
}
const styles = StyleSheet.create({
image: {
aspectRatio: 1,
backgroundColor: 'rgba(0,0,0,0.02)',
},
});
From above code, trying to navigate using:
import { useNavigation } from 'react-navigation-hooks';
getNav(){ return useNavigation(); }
this.getNav().navigate('Pager')
It errors our stating: Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component.
How do I navigate using "useNavigation()" or how do I get "this.props.navigation" reference in a component? (I am new to React Native, please help me understand)
Update
I tried using "this.props.navigation" and it gives me undefined error:
#sayog you are breaking the rule of hook brother.
Could you try this?
const [loading, setLoading] = useState(false);
const navigation = useNavigation();
make sure you put it after your state declaration
Note: Dont' use class component for Hook. use Functional Component for hook
//In Card component remove this
//import { useNavigation } from 'react-navigation-hooks';
//Add this line
import {withNavigation} from 'react-navigation';
class Card extends React.Component {
static propTypes = {
fullname: PropTypes.string.isRequired,
image: Image.propTypes.source.isRequired,
linkText: PropTypes.string.isRequired,
onPressLinkText: PropTypes.func.isRequired,
epicId: PropTypes.string.isRequired,
};
state = {
loading: true,
};
//Remove this
// getNav(){
// return useNavigation();
//}
handleLoad = () => {
this.setState({ loading: false });
};
render() {
const { fullname, image, linkText, onPressLinkText, epicId, prop } = this.props;
const { loading } = this.state;
return (
<View>
<AuthorRow
fullname={fullname}
linkText={linkText}
onPressLinkText={onPressLinkText}
/>
<TouchableHighlight onPress={() =>
//Make change like this
this.props.navigation.navigate('Pager')} underlayColor="white">
<View style={styles.image}>
{loading && (
<ActivityIndicator style={StyleSheet.absoluteFill} size={'large'} />
)}
<Image
style={StyleSheet.absoluteFill}
source={image}
onLoad={this.handleLoad}
/>
</View>
</TouchableHighlight>
</View>
);
}
}
const styles = StyleSheet.create({
image: {
aspectRatio: 1,
backgroundColor: 'rgba(0,0,0,0.02)',
},
});
//Make change like this
export default withNavigation(Card)
here is your mistake
mistake
onPress={() => this.getNav().navigate('Pager')
replace with
onPress={() => this.props.navigation.navigate('Pager')
here is the example
https://reactnavigation.org/docs/navigating/
https://medium.com/#marizu_makozi/navigating-between-screens-or-activities-using-react-navigation-library-68d57657d81
Another example code
class FirstScreen extends React.Component {
static navigationOptions = {
title: 'First',
}
componentDidMount(){
const {navigate} = this.props.navigation;
navigate('Second');
fetch('http://apiserver').then( (response) => {
navigate('Second');
});
}
render() {
return (
<AppLogo />
);
}
}
const AppNavigator = StackNavigator({
First: {
screen: FirstScreen,
},
Second: {
screen: SecondScreen,
},
});
You cannot use hooks inside Class component convert your class component to a functional component or use navigation object for navigations by passing navigation props to your component this.props.navigation.navigate('screenName')

ReactNative FlatList is not rendered properly in Arabic Right To Left

I have a FlatList to display list of users, when I change the device language to arabic, RTL is not rendered correctly. The arrows should be pointing the other way.
Here is the screenshot of how it looks on mobile.
Could you suggest if I am missing anything.
Here is my code.
FlatListScreen.js
import React, {Component} from 'react';
import {View, Text, FlatList} from 'react-native';
import {List, ListItem, SearchBar} from 'react-native-elements';
class FlatListScreen extends Component{
constructor(props) {
super(props);
this.state = {
loading: false,
date: [],
page: 1,
seed: 1,
error: null,
refreshing: false
};
}
componentDidMount(){
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const {page, seed } = this.state;
const url = 'https://randomuser.me/api/?seed=${seed}&page=${page}&results=20';
this.setState({loading: true});
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: page === 1 ? res.results : [...this.state.data, ...res.results],
error: res.error || null,
loading: false,
refreshing: false
});
})
.catch(error => {
this.setState({error, loading: false});
});
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '86%',
backgroundColor: '#CED0CE',
marginLeft: '14%',
}}
/>
);
};
renderFooter = () => {
if(!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderTopColor: "#CED0CE"
}}
/>
)
};
renderHeader = () => {
return <SearchBar
placeholder="Search.."
lightTheme
round />;
}
onLearnMore = (item) => {
this.props.navigation.navigate('Profile', { ...item });
};
render (){
return (
<List containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0}}>
<FlatList
data = {this.state.data}
renderItem={({item}) => (
<ListItem
roundAvatar
title={`${item.name.first} ${item.name.last}`}
subtitle={item.email}
avatar={{uri: item.picture.thumbnail}}
containerStyle={{ borderBottomWidth: 0 }}
onPress={() => this.onLearnMore(item)}
/>
)}
keyExtractor = {item => item.email}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
/>
</List>
)
}
}
export default FlatListScreen;
From the React-native documentation I have added following code for RTL support
https://facebook.github.io/react-native/blog/2016/08/19/right-to-left-support-for-react-native-apps.html#writing-rtl-ready-components
MainActivity.java
#Override
protected String getMainComponentName() {
I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance();
sharedI18nUtilInstance.allowRTL(context, true);
return "navapp";
}
AndroidManifest.xml
<application
android:name=".MainApplication"
android:label="#string/app_name"
android:icon="#mipmap/ic_launcher"
android:allowBackup="false"
android:theme="#style/AppTheme"
android:supportsRtl="true">
Arrows showing in your FlatList are coming from the ListItem component you are using to show every record in your data. You need to check for the current language/RTL support and render arrow accordingly.
To achieve desired behavior you can use rightIcon property for ListItem component.
rightIcon
icon configuration for right icon (optional), either a name from the
icon library (like material) or a React Native element like Image.
Shows up unless hideChevron is set
Sample
<ListItem
roundAvatar
title={'title'}
subtitle={'sub title'}
avatar={{uri: item.picture.thumbnail}}
containerStyle={{ borderBottomWidth: 0 }}
onPress={() => this.onLearnMore(item)}
rightIcon={{name: (I18nManager.isRTL ? 'chevron-left': 'chevron-right')}}
/>

this.props.navigation is undefined when using DrawerNavigator

I'm integrating DrawerNavigator of 'react-navigation' to my project as this document. But when I run the project it always get this error when click on the Button:
TypeError: undefined is not an object (evaluating
'this.props.navigation.navigate')
And when I swipe from left to right, nothing happen, no drawer open.
I've check this.props and it's always log empty {} in console.
I tried many solutions but it's still not working.
calculator.js
export default class Calculator extends Component {
constructor(props) {
super(props);
}
static navigationOptions = {
drawerLabel: 'Calculator',
drawerIcon: ({ tintColor }) => (
<Image
source={require('./../../res/images/icon_calculator.png')}
style={[styles.icon, {tintColor: tintColor}]}
/>
),
};
render() {
return (
<View style={styles.container}>
<View>
<Text style={styles.title}>Tip Calculator</Text>
</View>
<Button
onPress={() => this.props.navigation.navigate("SettingsScreen")}
title="Go to settings"
/>
</View>
);
}
}
module.exports = Calculator;
settings.js
export default class Settings extends Component {
constructor(props) {
super(props);
}
static navigationOptions = {
drawerLabel: 'Settings',
drawerIcon: ({ tintColor }) => (
<Image
source={require('./../../res/images/icon_settings.png')}
style={[styles.icon, {tintColor: tintColor}]}
/>
),
};
render() {
return (
<View style={styles.container}>
<Text>Settings</Text>
<Button
onPress={() => this.props.navigation.goBack()}
title="Go back home"
/>
</View>
);
}
}
module.exports = Settings;
navigation.js
import {
DrawerNavigator
} from 'react-navigation';
import Calculator from './../components/calculator/calculator.js';
import Settings from './../components/settings/settings.js';
const RootDrawer = DrawerNavigator({
CalculatorScreen: {
path: '/',
screen: Calculator
},
SettingsScreen: {
path: '/sent',
screen: Settings
}
}, {
initialRouteName: 'CalculatorScreen',
drawerPosition: 'left'
});
export default RootDrawer;
App.js
export default class App extends Component<{}> {
render() {
return (
<Calculator/>
);
}
}
index.js
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('rn_tip_calculator', () => App);
Do I have to use StackNavigator with DrawerNavigator, or am I missed something in config?
Full source code, it's only a simple example project, please have a look: https://github.com/HCMUS-IceTeaViet-SE/rn_tip_calculator
Any help will be appreciated. Thanks!
You can use dispatch api https://reactnavigation.org/docs/navigators/navigation-actions
1) import navigation actions
import { NavigationActions } from 'react-navigation'
2) dispatch navigation action:
const navigateAction = NavigationActions.navigate({
routeName: 'SettingsScreen',
params: {},
})
this.props.navigation.dispatch(navigateAction)
I'm pretty new to React-Native but sometimes this also happens to me.
I'm using Redux and stack navigator.... but here is my working example...
import { StackNavigator } from 'react-navigation'
import { Animated, Easing } from 'react-native'
import LoginScreen from '../Containers/LoginScreen'
import LaunchScreen from '../Containers/LaunchScreen'
import HomeScreen from '../Containers/HomeScreen'
import SignUpScreen from '../Containers/SignUpScreen'
import SettingsScreen from '../Containers/SettingsScreen'
import VehicleCreateScreen from '../Containers/VehicleCreateScreen'
import styles from './Styles/NavigationStyles'
// Manifest of possible screens
const PrimaryNav = StackNavigator({
LoginScreen: { screen: LoginScreen },
LaunchScreen: { screen: LaunchScreen },
HomeScreen: { screen: HomeScreen },
SignUpScreen: { screen: SignUpScreen },
SettingsScreen: { screen: SettingsScreen },
VehicleCreateScreen: { screen: VehicleCreateScreen }
}, {
// Default config for all screens
headerMode: 'none',
initialRouteName: 'LaunchScreen',
navigationOptions: {
headerStyle: styles.header
},
transitionSpec: {
duration: 0,
timing: Animated.timing,
easing: Easing.step0,
},
},
)
export default PrimaryNav
And then from a component not connected to REDUX
import React, { Component } from 'react';
import { Container, Content, List, ListItem, Icon, Text, Button, Left, Right, Badge } from 'native-base';
import { Image } from 'react-native'
import styles from './Styles/SideBarStyle';
// import backgroundImage from '../Images/vw.jpg'
const backgroundImage = require("../Images/vw.jpg");
const drawerImage = require("../Images/dirtyHandsDark.jpg");
export default class SideBar extends Component {
constructor(props) {
super(props);
}
render() {
// *********** HERE WE DECLARE AN ARRAY TO RENDER LISTS FROM. THIS COULD ALSO BE LIST OF BIKES FROM STORE.. ***********
const datas = [
{
name: "Home",
route: "HomeScreen",
icon: "settings",
bg: "#C5F442",
},
{
name: "Repair",
route: "HomeScreen",
icon: "settings",
bg: "#C5F442",
},
{
name: "My Profile",
route: "SettingsScreen",
icon: "settings",
bg: "#C5F442",
},
];
return (
<Container>
<Content bounces={false} style={{ flex: 1, backgroundColor: "#fff", top: -1 }}>
<Image source={backgroundImage} style={styles.drawerCover}>
<Image square style={styles.drawerImage} source={drawerImage} />
</Image>
<List
dataArray={datas}
renderRow={data =>
// *********** CREATE NEW LIST ITEM ON CLICK NAVIGATE TO APPROPRIATE LISTITEM.SCREEN ***********
<ListItem button noBorder onPress={() => this.props.navigation.navigate(data.route)}>
<Left>
<Icon active name={data.icon} style={{ color: "#777", fontSize: 26, width: 30 }} />
<Text style={styles.text}>
{data.name}
</Text>
</Left>
{data.types &&
<Right style={{ flex: 1 }}>
<Badge
style={{
borderRadius: 3,
height: 25,
width: 72,
backgroundColor: data.bg,
}}
>
<Text style={styles.badgeText}>{`${data.types} Types`}</Text>
</Badge>
</Right>}
</ListItem>}
/>
</Content>
</Container>
);
}
}
You can see I reference this.props.navigation.navigate no problem.
Heres my repo for reference.
https://github.com/GavinThomas1192/motoMechanicMeeKanic/tree/master/App
This happen because I don't "connect" my DrawerNavigator to my app.
There is 2 ways to achieve this:
First way: register DrawerNavigator as app root component. In index.js change:
from AppRegistry.registerComponent('rn_tip_calculator', () => App);
to AppRegistry.registerComponent('rn_tip_calculator', () => RootDrawer);
So you could delete App.js because it's useless now.
Second way: Keep register App component (in App.js) as app root component. Then put inside App component to "connect" DrawerNavigator to the app.
export default class App extends Component<{}> {
render() {
return (
<RootDrawer/>
);
}
}
The document say nothing about how to connect the navigator to the app, nothing about register component or put navigator inside root component. This drive newbie like me mad!

Categories

Resources