Skip to content
  • March 6, 2026
CS360Studio.com CS360STUDIO.COM

Web Development, Tech, News and more!

×
CS360Studio.com CS360STUDIO.COM

Web Development, Tech, News and more!

  • Home
  • About
  • Contact
  • Privacy Policy
  • Terms of Use
  • Home
  • 2021
  • April
  • Bootcamp Notes – Day 10 (Tues) – React Native: Week 4: Local Notifications, Social Sharing, and Picking an Image
Web Development What's New Local Notifications, Picking an Image, Social Sharing CS April 27, 2021

Bootcamp Notes – Day 10 (Tues) – React Native: Week 4: Local Notifications, Social Sharing, and Picking an Image

Local Notifications, Social Sharing, and Picking an Image

Local Notifications

Notifications are a common way of drawing attention by adding an icon to the notification bar and a message to the notification drawer.

  • Local notifications are generated from the client side
  • Push notifications are generated from the server side (but still use the same mechanism to display the notification on the client.)
  • The Expo Notifications APi provides access to both push (remote) notifications and local notifications.
  • Local notifications can be sent immediately, or at a scheduled time, either once or on a recurring basis
  • Example use case: An application that sends a notification to a user at a specific, recurring time, such as a reminder to exercise morning
  • Or a calendar-based app that sends an event reminder, or a notification about something happening in the app.

EXPO NOTIFICATIONS APi

  • Notifications.setNotificationHandler(..) – sets what to do with a notification that is received when the app is in foreground (default: does not show it)
  • Notifications.scheduleLocalNotificationAsync(…) – schedules a local notification to fire immediately or in the future, optionally repeating at a given interval
  • There are several other methods available from the Notifications APi, for generating push notifications, canceling a scheduled notification, etc

IN ADDITION

We will need to request permission to generate notifications from the underlying operating system:

  • Notifications.getPermissionsAsync()
  • Notifications.requestPermissionsAsync()

Local Notifications

Use the Expo Notifications API to generate local notifications, as well as get permissions from the device to send the local notifications.

eBay Electronics: Best tech for way less - Up to 50% off and free shipping
  • Install Expo Notifications: expo install expo-notifications@~0.3.3

ReservationComponent.js 

import React, { Component } from ‘react’;
import { Text, View, ScrollView, StyleSheet, Picker, Switch, Button, Alert } from ‘react-native’;
import DateTimePicker from ‘@react-native-community/datetimepicker’;
import * as Animatable from ‘react-native-animatable’;
import * as Notifications from ‘expo-notifications’;
class Reservation extends Component {
    constructor(props) {
        super(props);
        this.state = {
            campers: 1,
            hikeIn: false,
            date: new Date(),
            showCalendar: false,
        };
    }
    static navigationOptions = {
        title: ‘Reserve Campsite’
    }
    handleReservation() {
        Alert.alert(
                ‘Begin Search?’,
                ‘Number of Campers: ‘ + this.state.campers + “\n\n” + 
                ‘Hike-In? ‘ + this.state.hikeIn +  “\n\n” + 
                ‘Date: ‘+ this.state.date.toLocaleDateString(‘en-US’),
            [
                {
                    text: ‘Cancel’,
                    onPress: () => {
                        console.log(‘Reservation Search Canceled’);
                        this.resetForm();
                    },
                    style: ‘cancel’
                },
                {
                    text: ‘OK’, 
                    onPress: () => {
                        this.presentLocalNotification(this.state.date.toLocaleDateString(‘en-US’));
                        this.resetForm();
                    }
                }
            ],
            { cancelable: false }
        );
    }
    resetForm() {
        this.setState({
            campers: 1,
            hikeIn: false,
            date: new Date(),
            showCalendar: false,
            showModal: false
        });
    }
    async presentLocalNotification(date) {
        function sendNotification() {
            Notifications.setNotificationHandler({
                handleNotification: async () => ({
                    shouldShowAlert: true
                })
            });
            Notifications.scheduleNotificationAsync({
                content: {
                    title: ‘Your Campsite Reservation Search’,
                    body: `Search for ${date} requested`
                },
                trigger: null
            });
        }
        let permissions = await Notifications.getPermissionsAsync();
        if (!permissions.granted) {
            permissions = await Notifications.requestPermissionsAsync();
        }
        if (permissions.granted) {
            sendNotification();
        }
    }
    render() {
        return (
            <ScrollView>
                <Animatable.View
                    animation=’zoomIn’
                    duration={2000}
                    delay={1000}
                >
                    <View style={styles.formRow}>
                        <Text style={styles.formLabel}>Number of Campers</Text>
                        <Picker
                            style={styles.formItem}
                            selectedValue={this.state.campers}
                            onValueChange={itemValue => this.setState({campers: itemValue})}
                        >
                            <Picker.Item label=’1′ value=’1′ />
                            <Picker.Item label=’2′ value=’2′ />
                            <Picker.Item label=’3′ value=’3′ />
                            <Picker.Item label=’4′ value=’4′ />
                            <Picker.Item label=’5′ value=’5′ />
                            <Picker.Item label=’6′ value=’6′ />
                        </Picker>
                    </View>
                    <View style={styles.formRow}>
                        <Text style={styles.formLabel}>Hike-In?</Text>
                        <Switch
                            style={styles.formItem}
                            value={this.state.hikeIn}
                            trackColor={{true: ‘#5637DD’, false: null}}
                            onValueChange={value => this.setState({hikeIn: value})}
                        />
                    </View>
                    <View style={styles.formRow}>
                        <Text style={styles.formLabel}>Date</Text>
                        <Button
                            onPress={() =>
                                this.setState({showCalendar: !this.state.showCalendar})
                            }
                            title={this.state.date.toLocaleDateString(‘en-US’)}
                            color=’#5637DD’
                            accessibilityLabel=’Tap me to select a reservation date’
                        />
                    </View>
                    {this.state.showCalendar && (
                        <DateTimePicker
                            value={this.state.date}
                            mode={‘date’}
                            display=’default’
                            onChange={(event, selectedDate) => {
                                selectedDate && this.setState({date: selectedDate, showCalendar: false});
                            }}
                            style={styles.formItem}
                        />
                    )}
                    <View style={styles.formRow}>
                        <Button
                            onPress={() => this.handleReservation()}
                            title=’Search’
                            color=’#5637DD’
                            accessibilityLabel=’Tap me to search for available campsites to reserve’
                        />
                    </View>
                </Animatable.View>
            </ScrollView>
        );
    }
}
const styles = StyleSheet.create({
    formRow: {
        alignItems: ‘center’,
        justifyContent: ‘center’,
        flex: 1,
        flexDirection: ‘row’,
        margin: 20
    },
    formLabel: {
        fontSize: 18,
        flex: 2
    },
    formItem: {
        flex: 1
    }
});
export default Reservation;

Overview: Social Sharing

Mobile devices – and the internet itself – exists for us to share information with each other, and mobile devices have many ways to allow us to do that: email, text messaging, sharing information on social media accounts, etc

In the following exercises, we’ll look at two examples of how to share socially from within our app: the Expo SDK’s MailComposer APi to send emails, and React Native’s own Share APi to share to various apps.

The expo MailComposer APi allows you to fire up the MailComposer for your default email application on your device using a method called composeAsync. It takes one argument options which is an object with the following properties that can be set.

MailComposer.composeAsync(options)

options object:

  • recipients (array) — an array of email addresses
  • ccRecipients (array) — an array of email addresses
  • bccRecipients (array) — an array of email addresses
  • subject (string) — email subject
  • body (string) — email body
  • isHtml (boolean) — wheter the body contains HTML tags
    • (for formatting purposes – not working perfectly in Android)
  • attachments (array) — an array of file URi’s to attach

The composeAsync is an asynchronous method and it returns a promise that resolves to an object with the status property “sent“, “saved”, or “cancelled” for iOS. Android does not return this information and will always resolve to the status property “sent“.

REACT NATIVE SHARE APi

In the social sharing exercise, we will use the share APi from React Native. The share APi also has just one method and it is called share and it takes two arguments: content and options, which are both objects.

Share.share(content, options)

content object properties:

  • message
  • url
  • title

(required to have message OR url, or both)

options object contains miscellaneous config properties such as tintColor for iOS


Sending Email

  • Install and configure the Expo MailComposer API, along with a RNE Button component, to open the default email client on the device to a new email with the recipient email, subject, and body fields filled in.
  • Install MailComposer:  expo install expo-mail-composer

 

ContactComponent.js

import React, { Component } from ‘react’;
import { View, Text, ScrollView } from ‘react-native’;
import { Card, Button, Icon  } from ‘react-native-elements’;
import * as Animatable from ‘react-native-animatable’;
import * as MailComposer from ‘expo-mail-composer’;
class Contact extends Component {
    static navigationOptions = {
        title: ‘Contact Us’
    }
    sendMail() {
        MailComposer.composeAsync({
            recipients: [‘campsites@nucamp.co’],
            subject: ‘Inquiry’,
            body: ‘To whom it may concern:’
        })
    }
    render() {
        return (
            <ScrollView>
                <Animatable.View animation=’fadeInDown’ duration={2000} delay={1000}>  
                    <Card title=’Contact Information’ wrapperStyle={{margin: 20}}>
                            <Text>1 Nucamp Way </Text>
                            <Text>Seattle, WA 98001</Text>
                            <Text style={{marginBottom: 10}} >U.S.A.</Text>
                        
                            <Text>Phone: 1-206-555-1234 </Text>
                            <Text>Email: campsites@nucamp.co</Text>
                            <Button
                            title=”Send Email”
                            buttonStyle={{backgroundColor: ‘#5637DD’, margin: 40}}
                            icon={<Icon
                                name=’envelope-o’
                                type=’font-awesome’
                                color=’#fff’
                                iconStyle={{marginRight: 10}}
                            />}
                            onPress={() => this.sendMail()}
                        />
                    </Card>
               </Animatable.View>  
            </ScrollView>
        );
    }
}
export default Contact;

Social Sharing

  • Set up the React Native Share API to share information to social media accounts.

 

CampsiteInfoComponent.js

import React, { Component } from ‘react’;
import { Text, View, ScrollView, FlatList, Modal, Button, StyleSheet, Alert, PanResponder, Share } from ‘react-native’;
import { Card, Icon, Rating, Input } from ‘react-native-elements’;
import { connect } from ‘react-redux’;
import { baseUrl } from ‘../shared/baseUrl’;
import { postFavorite, postComment } from ‘../redux/ActionCreators’;
import * as Animatable from ‘react-native-animatable’;
const mapStateToProps = state => {
    return {
        campsites: state.campsites,
        comments: state.comments,
        favorites: state.favorites
    };
};
const mapDispatchToProps = {
    postFavorite: campsiteId => (postFavorite(campsiteId)),
    postComment: (campsiteId, rating, author, text) => postComment(campsiteId, rating, author, text)
};
function RenderCampsite(props) {
    const {campsite} = props;
    const view = React.createRef();
    const recognizeDrag = ({dx}) => (dx < -200) ? true : false;
    const recognizeComment = ({dx}) => (dx > 200) ? true : false;
    const panResponder = PanResponder.create({
        onStartShouldSetPanResponder: () => true,
        onPanResponderGrant: () => {
            view.current.rubberBand(1000)
            .then(endState => console.log(endState.finished ? ‘finished’ : ‘canceled’));
        },
        onPanResponderEnd: (e, gestureState) => {
            console.log(‘pan responder end’, gestureState);
            if (recognizeDrag(gestureState)) {
                Alert.alert(
                    ‘Add Favorite’,
                    ‘Are you sure you wish to add ‘ + campsite.name + ‘ to favorites?’,
                    [
                        {
                            text: ‘Cancel’,
                            style: ‘cancel’,
                            onPress: () => console.log(‘Cancel Pressed’)
                        },
                        {
                            text: ‘OK’,
                            onPress: () => props.favorite ?
                                console.log(‘Already set as a favorite’) : props.markFavorite()
                        }
                    ],
                    { cancelable: false }
                );
            } 
            else if (recognizeComment(gestureState)) {
                props.onShowModal()
            }
            return true;
        }
    });
    const shareCampsite = (title, message, url) => {
        Share.share({
            title: title,
            message: `${title}: ${message} ${url}`,
            url: url
        },{
            dialogTitle: ‘Share ‘ + title
        });
    };
    if (campsite) {
        return (
            <Animatable.View
                animation=’fadeInDown’
                duration={2000}
                delay={1000}
                ref={view}
                {…panResponder.panHandlers}>
            <Card 
                featuredTitle={campsite.name}
                image={{uri: baseUrl + campsite.image}}>
            
                <Text style={{margin: 10}}>
                    {campsite.description}
                </Text>
                <View style={styles.cardRow}>
                    <Icon
                        name={props.favorite ? ‘heart’ : ‘heart-o’}
                        type=’font-awesome’
                        color=’#f50′
                        raised
                        reverse
                        onPress={() => props.favorite ? 
                            console.log(‘Already set as a favorite’) : props.markFavorite()}
                    />
                    <Icon
                        name=’pencil’
                        type=’font-awesome’
                        color=’#5637DD’
                        raised
                        reverse
                        onPress={() => props.onShowModal()} 
                    />
                    <Icon
                            name={‘share’}
                            type=’font-awesome’
                            color=’#5637DD’
                            raised
                            reverse
                            onPress={() => shareCampsite(campsite.name, campsite.description, baseUrl + campsite.image)} 
                        />
                </View>
            </Card>
            </Animatable.View>
        );
    }
    return <View />;
}
function RenderComments({comments}) {
    const renderCommentItem = ({item}) => {
        return (
            <View style={{margin: 10}}>
                <Text style={{fontSize: 14}}>{item.text}</Text>
                <Rating
                        startingValue={item.rating}
                        imageSize={10}
                        style={{paddingVertical: ‘5%’, alignItems: ‘flex-start’}}
                        readonly
                    />
                <Text style={{fontSize: 12}}>{`– ${item.author}, ${item.date}`}</Text>
            </View>
        );
    };
    return (
        <Animatable.View animation=’fadeInUp’ duration={2000} delay={1000}>
            <Card title=’Comments’>
                <FlatList
                    data={comments}
                    renderItem={renderCommentItem}
                    keyExtractor={item => item.id.toString()}
                />
            </Card>
        </Animatable.View>
    );
}
class CampsiteInfo extends Component {
    constructor(props) {
        super(props);
        this.state = {
            showModal: false,
            rating: 5,
            author: ” “,
            text: ” “
        };
    }
    toggleModal() {
        this.setState({showModal: !this.state.showModal});
    }
    handleComment(campsiteId) {
        this.props.postComment(campsiteId, this.state.rating, this.state.author, this.state.text);
        this.toggleModal();
    }
    resetForm() {
        this.setState({
            showModal: false,
            rating: 5,
            author: ” “,
            text: ” “
        });
    }
    markFavorite(campsiteId) {
        this.props.postFavorite(campsiteId);
    }
    static navigationOptions = {
        title: ‘Campsite Information’
    }
    render() {
        const campsiteId = this.props.navigation.getParam(‘campsiteId’);
        const campsite = this.props.campsites.campsites.filter(campsite => campsite.id === campsiteId)[0];
        const comments = this.props.comments.comments.filter(comment => comment.campsiteId === campsiteId);
        return (
            <ScrollView>
                <RenderCampsite campsite={campsite}
                    favorite={this.props.favorites.includes(campsiteId)}
                    markFavorite={() => this.markFavorite(campsiteId)}
                    onShowModal={() => this.toggleModal(campsiteId)}
                />
                <RenderComments comments={comments} />
                <Modal
                    animationType={‘slide’}
                    transparent={false}
                    visible={this.state.showModal}
                    onRequestClose={() => this.toggleModal()}
                >
                    <View style={styles.modal}>
                    <Rating
                        showRating
                        startingValue={this.state.rating}
                        imageSize={40}
                        onFinishRating={rating => this.setState({rating: rating})} 
                        style={{paddingVertical: 10}}
                    />
                    <Input
                        placeholder=’Author’
                        leftIcon={{ type: “font-awesome”, name: “user-o”}}
                        leftIconContainerStyle={{paddingRight: 10}}
                        onChangeText={value => this.setState({author: value})}
                        value={this.state.author}
                        />
                    <Input
                        placeholder=’Comment’
                        leftIcon={{ type: “font-awesome”, name: “comment-o”}}
                        leftIconContainerStyle={{paddingRight: 10}}
                        onChangeText={value => this.setState({text: value})}
                        value={this.state.text}
                        />
                        <View style={{margin: 10}}>
                        <Button
                            onPress={() => {
                                this.handleComment(campsiteId);
                                this.resetForm();
                            }}
                            color=’#5637DD’
                            title=’Submit’
                        />
                        </View>
                        <View style={{margin: 10}}>
                        <Button
                            onPress={() => {
                                this.toggleModal();
                                this.resetForm();
                            }}
                            color=’#808080′
                            title=’Cancel’
                        />
                        </View>
                    </View>
                </Modal>
            </ScrollView>
        );
    }
}
const styles = {
    cardRow: {
        alignItems: ‘center’,
        justifyContent: ‘center’,
        flex: 1,
        flexDirection: ‘row’,
        margin: 20
    },
    modal: { 
        justifyContent: ‘center’,
        margin: 20
    }
};
export default connect(mapStateToProps, mapDispatchToProps)(CampsiteInfo);

Picking an Image

Mobile devices today are typically equipped with a camera, as well as an image gallery app for images saved to the device. The camera can be used for more than taking photos, ex. scanning a barcode or QR code. Expo SKD offers serveral APi’s to access the camera as well as access and manipulate saved images.

 

EXPO IMAGEPICKER APi

Provides access to system Ui to select image/video from the device’s library, or take a new photo or video as your selection:

ImagePicker.launchImageLibraryAsync(options)

ImagePicker.launchCameraAsync(options)

options object properties include:

  • mediaTypes: what type of media to pick, defaults to images
  • allowsEditing: boolean value, whether to present image editor after it is picked, defaults to false
  • aspect: the aspect ration to maintain if use is allowed to edit image
  • quality: quality of image compression and more

ImagePicker.launchCameraAsync(options)

ImagePicker.launchImageLibraryAsync(options)

  • The launchCameraAsync method will return a promise.
  • This promise is fulfilled when the user either takes a photo and confirms the choice, or cancels the phot taking operation.
  • Resolved promise: If a photo was taken, returns object with media uri, width and height, media type, and more
  • If no photo was taken because the operation was cancelled, the resolve object will only have one property, a cancelled property set to true — can check for this and respond appropriately.

 

EXPO IMAGEMANIPULATOR APi

  • use to alter an image
  • apply transforms: resize, rotate, flip, or Crop
  • configure compression level, format (JPEG or PNG)
  • whether to include image data in BASE64 format

 

EXPO CAMERA APi

  • Provides a React component that renders a preview for the device’s front or back camera
  • You can adjust zoom, autofocus, white balance, flash mode
  • You can take a photo, or start and stop video recording
  • detect faces, scan a barcode, and more

Picking an Image

  • Learn to use the ImagePicker API from Expo to obtain an image for use in the app by taking a photo with the mobile device’s camera.
  • Implement tabbed navigation.
  • Add a user registration form.
  • Install expo-image-picker, react-navigation-tabs, and expo-permissions:  expo install expo-image-picker@8 react-navigation-tabs expo-permissions@9

LoginComponent.js

import React, { Component } from 'react';
import { View, StyleSheet, ScrollView, Image } from 'react-native';
import { Input, CheckBox, Button, Icon } from 'react-native-elements';
import * as SecureStore from 'expo-secure-store';
import * as ImagePicker from 'expo-image-picker';
import * as Permissions from 'expo-permissions';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import { baseUrl } from '../shared/baseUrl';

class LoginTab extends Component {

    constructor(props) {
        super(props);

        this.state = {
            username: '',
            password: '',
            remember: false
        };
    }

    static navigationOptions = {
        title: 'Login',
        tabBarIcon: ({tintColor}) => (
            <Icon
                name='sign-in'
                type='font-awesome'
                iconStyle={{color: tintColor}}
            />
        )
    }

    handleLogin() {
        console.log(JSON.stringify(this.state));
        if (this.state.remember) {
            SecureStore.setItemAsync(
                'userinfo',
                JSON.stringify({
                    username: this.state.username,
                    password: this.state.password
                })
            ).catch(error => console.log('Could not save user info', error));
        } else {
            SecureStore.deleteItemAsync('userinfo').catch(
                error => console.log('Could not delete user info', error)
            );
        }
    }

    componentDidMount() {
        SecureStore.getItemAsync('userinfo')
            .then(userdata => {
                const userinfo = JSON.parse(userdata);
                if (userinfo) {
                    this.setState({username: userinfo.username});
                    this.setState({password: userinfo.password});
                    this.setState({remember: true})
                }
            });
    }

    render() {
        return (
            <View style={styles.container}>
                <Input
                    placeholder='Username'
                    leftIcon={{type: 'font-awesome', name: 'user-o'}}
                    onChangeText={username => this.setState({username})}
                    value={this.state.username}
                    containerStyle={styles.formInput}
                    leftIconContainerStyle={styles.formIcon}
                />
                <Input
                    placeholder='Password'
                    leftIcon={{type: 'font-awesome', name: 'key'}}
                    onChangeText={password => this.setState({password})}
                    value={this.state.password}
                    containerStyle={styles.formInput}
                    leftIconContainerStyle={styles.formIcon}
                />
                <CheckBox
                    title='Remember Me'
                    center
                    checked={this.state.remember}
                    onPress={() => this.setState({remember: !this.state.remember})}
                    containerStyle={styles.formCheckbox}
                />
                <View style={styles.formButton}>
                    <Button
                        onPress={() => this.handleLogin()}
                        title='Login'
                        icon={
                            <Icon
                                name='sign-in'
                                type='font-awesome'
                                color='#fff'
                                iconStyle={{marginRight: 10}}
                            />
                        }
                        buttonStyle={{backgroundColor: '#5637DD'}}
                    />
                </View>
                <View style={styles.formButton}>
                    <Button
                        onPress={() => this.props.navigation.navigate('Register')}
                        title='Register'
                        type='clear'
                        icon={
                            <Icon
                                name='user-plus'
                                type='font-awesome'
                                color='blue'
                                iconStyle={{marginRight: 10}}
                            />
                        }
                        titleStyle={{color: 'blue'}}
                    />
                </View>
            </View>
        );
    }
}

class RegisterTab extends Component {

    constructor(props) {
        super(props);

        this.state = {
            username: '',
            password: '',
            firstname: '',
            lastname: '',
            email: '',
            remember: false,
            imageUrl: baseUrl + 'images/logo.png'
        };
    }

    static navigationOptions = {
        title: 'Register',
        tabBarIcon: ({tintColor}) => (
            <Icon
                name='user-plus'
                type='font-awesome'
                iconStyle={{color: tintColor}}
            />
        )
    }

    getImageFromCamera = async () => {
        const cameraPermission = await Permissions.askAsync(Permissions.CAMERA);
        const cameraRollPermission = await Permissions.askAsync(Permissions.CAMERA_ROLL);

        if (cameraPermission.status === 'granted' && cameraRollPermission.status === 'granted') {
            const capturedImage = await ImagePicker.launchCameraAsync({
                allowsEditing: true,
                aspect: [1, 1]
            });
            if (!capturedImage.cancelled) {
                console.log(capturedImage);
                this.setState({imageUrl: capturedImage.uri});
            }
        }
    }

    handleRegister() {
        console.log(JSON.stringify(this.state));
        if (this.state.remember) {
            SecureStore.setItemAsync('userinfo', JSON.stringify(
                {username: this.state.username, password: this.state.password}))
                .catch(error => console.log('Could not save user info', error));
        } else {
            SecureStore.deleteItemAsync('userinfo').catch(
                error => console.log('Could not delete user info', error)
            );
        }
    }

    render() {
        return (
            <ScrollView>
                <View style={styles.container}>
                    <View style={styles.imageContainer}>
                        <Image
                            source={{uri: this.state.imageUrl}}
                            loadingIndicatorSource={require('./images/logo.png')}
                            style={styles.image}
                        />
                        <Button
                            title='Camera'
                            onPress={this.getImageFromCamera}
                        />
                    </View>
                    <Input
                        placeholder='Username'
                        leftIcon={{type: 'font-awesome', name: 'user-o'}}
                        onChangeText={username => this.setState({username})}
                        value={this.state.username}
                        containerStyle={styles.formInput}
                        leftIconContainerStyle={styles.formIcon}
                    />
                    <Input
                        placeholder='Password'
                        leftIcon={{type: 'font-awesome', name: 'key'}}
                        onChangeText={password => this.setState({password})}
                        value={this.state.password}
                        containerStyle={styles.formInput}
                        leftIconContainerStyle={styles.formIcon}
                    />
                    <Input
                        placeholder='First Name'
                        leftIcon={{type: 'font-awesome', name: 'user-o'}}
                        onChangeText={firstname => this.setState({firstname})}
                        value={this.state.firstname}
                        containerStyle={styles.formInput}
                        leftIconContainerStyle={styles.formIcon}
                    />
                    <Input
                        placeholder='Last Name'
                        leftIcon={{type: 'font-awesome', name: 'user-o'}}
                        onChangeText={lastname => this.setState({lastname})}
                        value={this.state.lastname}
                        containerStyle={styles.formInput}
                        leftIconContainerStyle={styles.formIcon}
                    />
                    <Input
                        placeholder='Email'
                        leftIcon={{type: 'font-awesome', name: 'envelope-o'}}
                        onChangeText={email => this.setState({email})}
                        value={this.state.email}
                        containerStyle={styles.formInput}
                        leftIconContainerStyle={styles.formIcon}
                    />
                    <CheckBox
                        title='Remember Me'
                        center
                        checked={this.state.remember}
                        onPress={() => this.setState({remember: !this.state.remember})}
                        containerStyle={styles.formCheckbox}
                    />
                    <View style={styles.formButton}>
                        <Button
                            onPress={() => this.handleRegister()}
                            title='Register'
                            icon={
                                <Icon
                                    name='user-plus'
                                    type='font-awesome'
                                    color='#fff'
                                    iconStyle={{marginRight: 10}}
                                />
                            }
                            buttonStyle={{backgroundColor: '#5637DD'}}
                        />
                    </View>
                </View>
            </ScrollView>
        );
    }
}

const Login = createBottomTabNavigator(
    {
        Login: LoginTab,
        Register: RegisterTab
    },
    {
        tabBarOptions: {
            activeBackgroundColor: '#5637DD',
            inactiveBackgroundColor: '#CEC8FF',
            activeTintColor: '#fff',
            inactiveTintColor: '#808080',
            labelStyle: {fontSize: 16}
        }
    }
);

const styles = StyleSheet.create({
    container: {
        justifyContent: 'center',
        margin: 10
    },
    formIcon: {
        marginRight: 10
    },
    formInput: {
        padding: 8
    },
    formCheckbox: {
        margin: 8,
        backgroundColor: null
    },
    formButton: {
        margin: 20,
        marginRight: 40,
        marginLeft: 40
    },
    imageContainer: {
        flex: 1,
        flexDirection: 'row',
        alignItems: 'center',
        justifyContent: 'space-evenly',
        margin: 10
    },
    image: {
        width: 60,
        height: 60
    }
});

export default Login;

Additional Resources:

  • Expo – Notifications 
  • MDN – Making asynchronous programming easier with async and await
  • MDN – async function
  • MDN – await operator
  • developers.google.com – Async functions 
  • JavaScript.info – Async/Await
  • Expo – MailComposer
  • RNE – Button, Icon
  • React Native – Share
  • BAMTech – Sharing Content with React Native
  • xpo – ImagePicker
  • Expo – Permissions
  • React Navigation – createBottomTabNavigator
  • React Navigation – Guides – Tab navigation
Tag Local Notifications Picking an Image Social Sharing
Previous post

Bootcamp Notes – Day 9 (Mon) – React Native: Week 4: Accessing Native Capabilities of Devices: The Expo SDK

Next post

Behind The Music

Related Posts

CS 0

Libon Open Chat takes on WhatsApp with HTML5 IM tech

March 6, 2026
CS 0

Remember how WALL-E turned trash into bricks? The Clear Drop Soft Plastic Compactor does too. #TITW

March 6, 2026
CS 0

Opatix – Tailwind CSS 3 Admin Layout & UI Kit Template

March 6, 2026

You May Have Missed

BEYOND THE RESET - Animated Short Film
Tech & More News Videos What's New

BEYOND THE RESET – Animated Short Film – That Exposes Great Reset! 1 Million Views!

April 18, 2023 CS
Project Veritas’ James O’Keefe Discusses State Of Journalism, Details Defamation Lawsuit Against NYT
Tech & More News Videos

Project Veritas’ James O’Keefe Discusses State Of Journalism, Details Defamation Lawsuit Against NYT

January 23, 2022 CS
China's Real Estate Crisis Spreads To The US!
Finance Videos What's New

China’s Real Estate Crisis Spreads To The US!

January 23, 2022 CS
livestream
Faith Videos

Orlando Church of Christ Sunday Service

January 23, 2022 CS
DFW Church Service 01/23/20222
Faith Videos

DFW Church Service 01/23/20222

January 23, 2022 CS
Jon Favreau Reveals Horrible Story Of Kathleen Kennedy! NEW Details Arrive (Star Wars Explained)
Tech & More News Videos

Jon Favreau Reveals Horrible Story Of Kathleen Kennedy! NEW Details Arrive (Star Wars Explained)

January 20, 2022 CS
AngularJS is Dead
Videos Web Development

AngularJS is Dead

January 19, 2022
Love Perseveres | Story of the Week
Faith Videos

Love Perseveres | Story of the Week

January 19, 2022
Goldman Sachs Just Collapsed!
Finance Videos

Goldman Sachs Just Collapsed!

January 18, 2022
STACKr News Weekly: Web Dev 2022, JS One-Liners, 3d JS
Videos Web Development

STACKr News Weekly: Web Dev 2022, JS One-Liners, 3d JS

January 18, 2022
  • What’s New
  • Web Development
  • Tech & News!
  • Finance
  • Crypto
  • Faith
  • Jesus Speaks

NewsBlogger - Magazine & Blog WordPress Theme 2026 | Powered By SpiceThemes

We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept”, you consent to the use of ALL the cookies.
Cookie settingsACCEPT
Manage consent

Privacy Overview

This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary
Always Enabled
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Non-necessary
Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.
SAVE & ACCEPT