React-Native TabNavigator and Modals - reactjs

I'm trying to use this library to show a custom modal dialog. I've a StackNavigator with three screens and on of these, the MainScreen, is a TabNavigator on which I set up the following header:
static navigationOptions = ({navigation}) => {
const { params } = navigation.state
return {
headerRight: (
<Content>
<Grid>
<Col style={styles.headerButton}>
<TouchableHighlight style={styles.infoButton} onPress={() => {params._onAbout()}} underlayColor='lightgrey'>
<Icon ios='ios-information-circle' android='md-information-circle' style={{fontSize: 24}} />
</TouchableHighlight>
</Col>
<Col style={styles.headerButton}>
<TouchableHighlight style={styles.logoutButton} onPress={() => {params._onLogout()}} underlayColor='lightgrey'>
<Icon ios='ios-exit-outline' android='md-exit' style={{fontSize: 24}} />
</TouchableHighlight>
</Col>
</Grid>
</Content>
)
}
}
The second button opens a simple Alert (from react-native). With the first button I would open a custom modal to show app and developer details.
The main screen has the following render method;
render(): JSX.Element {
return (
<TabContent />
)
}
where TabContent is simply my tabs configuration:
const TabContent: NavigationContainer = TabNavigator({
Courses: {
screen: CoursesScreen,
navigationOptions: ({ navigation }) => ({
// title: `${navigation.state.params.user}'s Courses`,
tabBarLabel: 'Corsi',
tabBarIcon: ({ tintColor, focused }) => (
<Icon ios={focused ? 'ios-calendar' : 'ios-calendar-outline'} android='md-calendar' style={{fontSize: 18, color: tintColor}} />
)
})
},
Profile: {
screen: ProfileScreen,
navigationOptions: ({ navigation }) => ({
// title: `${navigation.state.params.user}'s Profile`,
tabBarLabel: 'Profilo',
tabBarIcon: ({ focused, tintColor }) => (
<Icon ios={focused ? 'ios-person' : 'ios-person-outline'} android='md-person' style={{fontSize: 18, color: tintColor}} />
)
})
}
}, {
tabBarOptions: {
activeTintColor: '#F3E03B',
showIcon: true,
labelStyle: {
fontWeight: 'bold'
},
style: {
backgroundColor: 'black'
}
}
})
The library linked above requires a layout like this:
<View style={styles.wrapper}>
<Modal style={[styles.modal, styles.modal3]} position={"center"} ref={"modal3"} isDisabled={this.state.isDisabled}>
<Text style={styles.text}>Modal centered</Text>
<Button onPress={() => this.setState({isDisabled: !this.state.isDisabled})} style={styles.btn}>Disable ({this.state.isDisabled ? "true" : "false"})</Button>
</Modal>
</View>
but if I put TabContent tab inside that view the tab navigator doesn't work anymore.
Is there a way to make my TabNavigator and Modal from that library work together?

I found a solution.
Using Container as root component allow to nest the TabContent aside other components:
render(): JSX.Element {
return (
<Container>
<Spinner visible={this.props.isLoggingOut} textContent={'Disconnessione in corso...'} textStyle={{color: '#FFF'}} />
<TabContent screenProps={{ isAdmin: this.props.isAdmin }} />
<Modal style={styles.aboutModal} position={'center'} ref={'aboutModal'} isDisabled={false}>
<Text>Modal centered</Text>
</Modal>
</Container>
)
}

Related

Setting accessibility labels with React.Navigator

I am working on accessibility labels for the voice assistant, I've done most of the tags but I can not set the bottom tab navigator options, how can I use accessibility labels with the below BottomTab.Navigator components?
In the below, I could set the labels with the Icons but it is encapslating limited area.
When I try with Bottom.Navigator, throwing errors something like that:
Error: A navigator can only contain 'Screen' components as its direct children (found 'undefined'). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.
export default function BottomTabNavigator(): any {
const colorScheme = useColorScheme();
return (
<BottomTab.Navigator
initialRouteName={Routes.TAB_ONE}
tabBarOptions={{
showLabel: false,
inactiveBackgroundColor: Colors[colorScheme].secondaryColor,
activeBackgroundColor: Colors[colorScheme].secondaryColor,
style: {
borderTopWidth: 0,
borderTopColor: 'transparent',
height: 50,
},
}}
>
<BottomTab.Screen
name={Routes.TAB_ONE}
component={TabOneNavigator}
options={{
tabBarIcon: () => (
<View accessible={true} accessibilityLabel="home page">
<CustomIcon name="home" color={Colors[colorScheme].buttonText} />
</View>
),
}}
/>
<BottomTab.Screen
name={Routes.TAB_TWO}
component={TabTwoNavigator}
options={{
tabBarIcon: () => (
<View accessible={true} accessibilityLabel="downloads">
<CustomIcon
name="library"
color={Colors[colorScheme].buttonText}
/>
</View>
),
}}
/>
<BottomTab.Screen
name={Routes.TAB_THREE}
component={TabThreeNavigator}
options={{
tabBarIcon: () => (
<View accessible={true} accessibilityLabel="menu">
<CustomIcon name="menu" color={Colors[colorScheme].buttonText} />
</View>
),
}}
/>
</BottomTab.Navigator>
);
}
Thanks in advance

How to place element over Bottom Tabs Navigator in React Native

I want to place Facebook ads to React native using react-native-fbads. I am using Bottom tab navigator in my application and I want the ad to be fixed floating over the bottom tab.
I can place the ad on every tab screen but I don't want to place the ad on every screen and instead, I want to place it just over the bottom tab so that it is visible on every tab user visits.
Something like this:
Code: TabNavigator.js
<Tab.Navigator
screenOptions={({ route }) => ({
headerShown: false,
})}
tabBarOptions={{
activeTintColor: '#001B79',
inactiveTintColor: 'gray',
}}
>
<Tab.Screen name="Home1" component={Home11} />
<Tab.Screen name="Home2" component={Home12} />
</Tab.Navigator>
I want to add <BannerAd /> so that it will work like I want it to be.
just try to use bottom-tab-navigator! regularly!
// ...
import { View, Text, TouchableOpacity } from 'react-native';
function MyTabBar({ state, descriptors, navigation }) {
return (
<View style={{ flexDirection: 'row' }}>
{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) {
// The `merge: true` option makes sure that the params inside the tab screen are preserved
navigation.navigate({ name: route.name, merge: true });
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
return (
<TouchableOpacity
accessibilityRole="button"
accessibilityState={isFocused ? { selected: true } : {}}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
style={{ flex: 1 }}
>
<Text style={{ color: isFocused ? '#673ab7' : '#222' }}>
{label}
</Text>
</TouchableOpacity>
);
})}
</View>
);
}
<Tab.Navigator tabBar={props => <MyTabBar {...props} />}>
{...}
</Tab.Navigator>

Navigation with parameters from custom element in Flatlist in React Native: Empty parameters

After not succeeding in solving my problem here, I had to create a new question with all my classes in it.
I am new to react native and have a problem figuring out how to navigate from one class to another one with passing parameters and would appreciate your help.
All I want to do is:
SessionCreate with flatlist containing CustomButton
Navigate from SessionCreate to ItemConfig by clicking CustomButton
Pass parameter "element" to ItemConfig
Show content of parameter passed in ItemConfig
With this setup an empty "element" is passed as parameter to the ItemConfigScreen (but no error occurs):
app.js:
//Views
function Home({ navigation }) {
return (
<HomeScreen />
);
}
function Session({ navigation }) {
return (
<SessionScreen />
);
}
//subviews
function SessionCreate({ navigation }) {
return (
<SessionCreateScreen />
);
}
function ItemConfig({ navigation }) {
return (
<ItemConfigScreen />
);
}
//navigation stacks
const SessionStack = createStackNavigator();
function SessionStackScreen({ navigation }) {
return (
<SessionStack.Navigator>
<SessionStack.Screen
name="Session"
component={Session}
options={{ tabBarLabel: 'Session!' }}
/>
<SessionStack.Screen
name="SessionCreate"
component={SessionCreate}
options={{ tabBarLabel: 'SessionCreate!' }}
/>
<SessionStack.Screen
name="ItemConfig"
component={ItemConfig}
options={{ tabBarLabel: 'ItemConfig!' }}
/>
</SessionStack.Navigator>
);
}
//Navbar Bottom
const Tab = createBottomTabNavigator();
function App() {
return (
<View style={[theme.colcontainer, { flexDirection: "column" }]} >
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused ? 'home' : 'home-outline';
} else if (route.name === 'Session') {
iconName = focused ? 'book' : 'book-outline';
}
// dynamic ionicon
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
>
<Tab.Screen name="Home" component={Home} />
<Tab.Screen name="Session" component={SessionStackScreen} />
</Tab.Navigator>
</NavigationContainer>
</View >
);
}
export default App;
SessionScreen.js:
function SessionScreen() {
const navigation = useNavigation();
return (
<View style={[theme.container, { flexDirection: "column" }]} >
<View>
<TouchableOpacity onPress={() => navigation.navigate('SessionCreate')}>
<Text >Create Session</Text>
</TouchableOpacity>
</View>
</View >
);
}
export default SessionScreen;
SessionCreateScreen.js:
//data
const sessionElements = [
{
id: "1",
title: "title1"
}
];
function SessionCreateScreen() {
const navigation = useNavigation()
const renderItemConfiguration = ({ item }) => (
<CustomButton element={item.title} onPress={() => navigation.navigate('ItemConfig', { element: 'item.title' })} />
);
return (
<View style={{ flexDirection: "column", flex: 2}} >
<SafeAreaView >
<FlatList
data={sessionElements}
renderItem={renderItemConfiguration}
keyExtractor={(item) => item.id}
/>
</SafeAreaView>
</View >
);
}
export default SessionCreateScreen;
ItemConfigScreen.js:
const element = "";
function ItemConfigScreen() {
return (
<ScrollView >
<View style={{ flexDirection: "column", flex: 2}} >
<Text>Configure {element} here</Text>
</View >
</ScrollView>
);
}
export default ItemConfigScreen;
Any help is appreciated.
To get parameters in ItemConfigScreen you have to use the useRoute hook from the react-navigation package.
you can read more about it here useRoute
import {useRoute} from '#react-navigation/native';
function ItemConfigScreen() {
const route = useRoute();
const element = route.params?.element;
return (
<ScrollView >
<View style={{ flexDirection: "column", flex: 2}} >
<Text>Configure {element} here</Text>
</View >
</ScrollView>
);
}
There is also a mistake in your onPress navigation call in CustomButton, instead of 'item.title' you will have to pass ${item.title} then only actual data will be passed. JS Template Literals
<CustomButton element={item.title} onPress={() => navigation.navigate('ItemConfig', { element: `${item.title}` })} />

createDrawerNavigator and back button

We're using drawer navigator in our app to show the routes in the drawer. Our routes have stacks of pages and users can go into a page. E.g.:
Home
HomeChild1
HomeChild2
About
When user goes to the HomeChild1 or HomeChild2 we want to show back button in the header. We don't want to add a back button in each page's navigation options like below:
HomeScreen1.navigationOptions = ({ navigation }) => {
const { state } = navigation;
return {
title: `Home Screen 1`,
headerLeft: (
<Icon
name="ios-arrow-back"
type="ionicon"
color="#FFF"
underlayColor="transparent"
iconStyle={{ paddingRight: 5 }}
onPress={() => {
navigation.navigate.goBack();
}}
/>
)
};
}
Is there a way to put this config at the global level - like in the defaultNavigationOptions.
const defaultNavigationOptions = ({ navigation }) => {
return {
hideStatusBar: false,
headerStyle: {
backgroundColor: Colors.baseColor
},
headerTintColor: Colors.titleColor,
headerBackImage: ( //this has no affect
<Icon
name="ios-arrow-back"
type="Ionicons"
style={{ color: "#D8025E", fontSize: 30, paddingHorizontal: 10 }}
/>
),
headerLeft: (
<Icon
name="menu"
size={30}
style={{ marginStart: 25 }}
color="#FFF"
backgroundColor="#FFF"
onPress={() => navigation.openDrawer()}
/>
)
};
};
You can set up using navigation path values.
defaultNavigationOptions: ({ navigation }) => ({
headerLeft: () => {
const { routeName } = navigation.state;
let iconName;
if (routeName === "HomeChild1") {
iconName = "ios-arrow-back";
} else if (routeName === "HomeChild2") {
iconName = "ios-arrow-back";
}
....
return (
<Icon
name={iconName}
size={30}
style={{ marginStart: 25 }}
color="#FFF"
backgroundColor="#FFF"
onPress={() => navigation.goBack()}
/>
);
...

React Navigation how to pass props to TabNavigator Stateless Functional Components

I am learning to use React Navigation and loving it, but can't figure out how to send props from my top level App Component down to my screen components. I could be (most probably) going about it completely the wrong way. Here is my code.
Main App Component
class App extends Component {
constructor(props) {
super(props);
this.state = {
signedIn: false,
checkedSignIn: false
};
}
componentWillMount() {
isSignedIn()
.then(res => this.setState({ signedIn: res, checkedSignIn: true }))
.catch(err => alert(err));
}
render() {
const { checkedSignIn, signedIn } = this.state;
if (!checkedSignIn) {
return null;
}
if (signedIn) {
console.log("yeah boi");
console.log(SignedOut);
return (
<Provider store={store}>
<SignedIn screenProps={(name = "TestName")} />
</Provider>
);
} else {
console.log("nah bro");
return (
<Provider store={store}>
<SignedOut />
</Provider>
);
}
}
}
Screen
export default ({ navigation }) =>
<View style={{ paddingVertical: 20 }}>
<Card title="John Doe">
<View
style={{
backgroundColor: "#bcbec1",
alignItems: "center",
justifyContent: "center",
width: 80,
height: 80,
borderRadius: 40,
alignSelf: "center",
marginBottom: 20
}}
>
<Text style={{ color: "white", fontSize: 28 }}>JD</Text>
</View>
<Button
title="SIGN OUT"
onPress={() =>
onSignOut().then(() => navigation.navigate("SignedOut"))} // NEW LOGIC
/>
</Card>
</View>;
Nav
export const SignedIn = TabNavigator({
Tasks: {
screen: Tasks,
navigationOptions: {
tabBarIcon: ({ tintColor }) =>
<SimpleLineIcons name="list" size={30} />
}
},
Home: {
screen: Home,
navigationOptions: {
tabBarIcon: ({ tintColor }) =>
<SimpleLineIcons name="home" size={30} />
}
},
Message: {
screen: Message,
navigationOptions: {
tabBarIcon: ({ tintColor }) =>
<SimpleLineIcons name="envelope-letter" size={30} />
}
},
Profile: {
screen: Profile,
navigationOptions: {
tabBarIcon: ({ tintColor }) =>
<SimpleLineIcons name="user" size={30} />
}
}
});
Can anyone tell me how I would pass props, such as my attempted {(name = "TestName")}, to the SignedIn SFC?
I am fairly new to react so please be gentle :)
Thanks
Sam
Got it sorted while still keeping the items stateless, using React Navigations screenProps parameter. Just had to fix my syntax in the Nav component and explicitly call screenProps in my screen. Here it is for reference:
Main App
class App extends Component {
constructor(props) {
super(props);
this.state = {
signedIn: false,
checkedSignIn: false
};
}
componentWillMount() {
isSignedIn()
.then(res => this.setState({ signedIn: res, checkedSignIn: true }))
.catch(err => alert(err));
}
render() {
const { checkedSignIn, signedIn } = this.state;
if (!checkedSignIn) {
return null;
}
if (signedIn) {
console.log("yeah boi");
console.log(SignedOut);
return (
<Provider store={store}>
<SignedIn screenProps={{ name: "TestName" }} />
</Provider>
);
} else {
console.log("nah bro");
return (
<Provider store={store}>
<SignedOut />
</Provider>
);
}
}
}
Screen
export default ({ navigation, screenProps }) =>
<View style={{ paddingVertical: 20 }}>
<Card title={screenProps.name}>
<View
style={{
backgroundColor: "#bcbec1",
alignItems: "center",
justifyContent: "center",
width: 80,
height: 80,
borderRadius: 40,
alignSelf: "center",
marginBottom: 20
}}
>
<Text style={{ color: "white", fontSize: 28 }}>JD</Text>
</View>
<Button
title="SIGN OUT"
onPress={() =>
onSignOut().then(() => navigation.navigate("SignedOut"))} // NEW LOGIC
/>
</Card>
</View>;
Nav
export const SignedIn = TabNavigator({
Tasks: {
screen: Tasks,
navigationOptions: {
tabBarIcon: ({ tintColor }) =>
<SimpleLineIcons name="list" size={30} />
}
},
Home: {
screen: Home,
navigationOptions: {
tabBarIcon: ({ tintColor }) =>
<SimpleLineIcons name="home" size={30} />
}
},
Message: {
screen: Message,
navigationOptions: {
tabBarIcon: ({ tintColor }) =>
<SimpleLineIcons name="envelope-letter" size={30} />
}
},
Profile: {
screen: Profile,
navigationOptions: {
tabBarIcon: ({ tintColor }) =>
<SimpleLineIcons name="user" size={30} />
}
}
});
Use store state to get data, set state of store in top level component,
use that state in your screen component
I think <SignedIn screenProps={(name = "TestName")} /> will rise a syntax error. It should be just <SignedIn name='TestName' />. The bigger problem is how you use the TabNavigator component. What if you try the following:
export const SignedIn = ({ name }) => TabNavigator({
Tasks: {
screen: Tasks,
navigationOptions: {
tabBarIcon: ({ tintColor }) =>
<SimpleLineIcons name={ name } size={30} />
}
},
Home: {
screen: Home,
navigationOptions: {
tabBarIcon: ({ tintColor }) =>
<SimpleLineIcons name={ name } size={30} />
}
},
Message: {
screen: Message,
navigationOptions: {
tabBarIcon: ({ tintColor }) =>
<SimpleLineIcons name={ name } size={30} />
}
},
Profile: {
screen: Profile,
navigationOptions: {
tabBarIcon: ({ tintColor }) =>
<SimpleLineIcons name={ name } size={30} />
}
}
});

Resources