React Native MasteryThe Ultimate React Native and Expo Course 🚀

Virtual Events app with React Native (notJust.Hack Workshop)

Vadim Savin profile picture
Vadim SavinFeb 29, 2024

Let’s build a Virtual Event Application with React Native. Shall we?

You can follow this build in video format here:

If you want to follow along, and build this application yourself, make sure to download the Asset bundle that contains all the dummy data, pre-defined components, PDF presentation and more.

Download the Asset Bundle 👇

To get the Source Code, PDF guide, dummy data, images, etc.

Context

This project is part of the notJust.Hack event, and the goal is to show you that it is possible to build a functional application in just 3 days.

For that reason, I will approach this project in a similar way you would approach a Hackathon project.

The Problem:
Organizing a multi day event such as notJust.Hack, I encounter the problem of sharing the schedule of the event, so that attendees will know what to expect when.

The Solution:
A mobile app that will display information about a Virtual Event, will show a clear schedule and let people RSPV to the sessions that they are interested in.

Attendees will have the possibility to chat and network in the application.

Let’s initialize the App using expo

BASH
npx create-expo-app VirtualEvents --template tabs
  • Start the development server
BASH
npm start
  • Run the applicaton by pressing i to run on iOS device/emulator or a to run on android device/simulator.
  • You can also scan the QR code using Expo Go App on your physical device

Calendar

For the calendar view, we will use the React Native Calendar library. Check out the docs here

  1. Install

    BASH
    npx expo install react-native-calendars
  2. Render an Agenda inside the screens/TabOneScreen.tsx

    JAVASCRIPT
    import { Alert, Pressable, StyleSheet } from "react-native";
    import { Text, View } from "../components/Themed";
    import { RootTabScreenProps } from "../types";
    import {
    Agenda,
    AgendaEntry,
    AgendaSchedule,
    DateData,
    } from "react-native-calendars";
    import { useState } from "react";
    import events from "../assets/data/events.json";
    export default function TabOneScreen({
    navigation,
    }: RootTabScreenProps<"TabOne">) {
    const [items, setItems] = useState<AgendaSchedule>({});
    const loadItems = (day: DateData) => {
    setItems(events);
    };
    const renderEmptyDate = () => {
    return (
    <View style={styles.emptyDate}>
    <Text>This is empty date!</Text>
    </View>
    );
    };
    const renderItem = (reservation: AgendaEntry, isFirst: boolean) => {
    const fontSize = isFirst ? 16 : 14;
    const color = isFirst ? "black" : "#43515c";
    return (
    <Pressable
    style={[styles.item, { height: reservation.height }]}
    onPress={() => Alert.alert(reservation.name)}
    >
    <Text style={{ fontSize, color }}>{reservation.name}</Text>
    </Pressable>
    );
    };
    return (
    <View style={styles.container}>
    <Agenda
    items={items}
    selected={"2022-11-25"}
    renderItem={renderItem}
    renderEmptyDate={renderEmptyDate}
    loadItemsForMonth={loadItems}
    // showOnlySelectedDayItems
    />
    </View>
    );
    }
    const styles = StyleSheet.create({
    container: {
    flex: 1,
    },
    item: {
    backgroundColor: "white",
    flex: 1,
    borderRadius: 5,
    padding: 10,
    marginRight: 10,
    marginTop: 17,
    },
    emptyDate: {
    height: 15,
    flex: 1,
    paddingTop: 30,
    },
    });

Event detailed page

Let’s display the details of the event in a Modal. For that, we can use the existing screens/ModalScreen.tsx

  • Open the modal when we press on an item in the Agenda List
JAVASCRIPT
onPress={() => navigation.navigate("Modal", {id: event.id})}
  • Now, let’s Render the deatils of the event
JAVASCRIPT
import { AntDesign } from "@expo/vector-icons";
import { StatusBar } from "expo-status-bar";
import { Platform, StyleSheet, Image } from "react-native";
import { Text, View } from "../components/Themed";
import { RootStackScreenProps } from "../types";
import CustomButton from "../components/CustomButton";
import users from "../assets/data/users.json";
import event from "../assets/data/event.json";
export default function ModalScreen({
route,
navigation,
}: RootStackScreenProps<"Modal">) {
const id = route.params.id;
console.log("Rendering event ", id);
const onJoin = () => {};
return (
<View style={styles.container}>
<Text style={styles.title}>{event.name}</Text>
<Text style={styles.time}>
<AntDesign name="calendar" size={24} color={"black"} />
{" | "}
{new Date(event.date).toDateString()}
</Text>
<View style={styles.footer}>
<Text style={styles.subtitle}>Attendees</Text>
<View style={styles.users}>
{users?.map((user, i) => (
<Image
source={{ uri: user.avatarUrl }}
style={[
styles.userAvatar,
{ transform: [{ translateX: -15 * i }] },
]}
key={user.id}
/>
))}
<View
style={[
styles.userAvatar,
{
transform: [{ translateX: -15 * users.length }],
},
]}
>
<Text>+{users.length}</Text>
</View>
</View>
<CustomButton text="Join the event" onPress={onJoin} />
</View>
{/* Use a light status bar on iOS to account for the black space above the modal */}
<StatusBar style={Platform.OS === "ios" ? "light" : "auto"} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 10,
paddingBottom: 25,
},
title: {
fontSize: 24,
fontWeight: "bold",
marginVertical: 10,
},
subtitle: {
fontSize: 18,
fontWeight: "bold",
},
time: {
fontSize: 20,
},
footer: {
marginTop: "auto",
},
users: {
flexDirection: "row",
marginVertical: 10,
},
userAvatar: {
width: 50,
aspectRatio: 1,
borderRadius: 30,
margin: 2,
borderColor: "white",
borderWidth: 2,
justifyContent: "center",
alignItems: "center",
backgroundColor: "gainsboro",
},
});

Render Users

  • Create a new component components/UserListItem.tsx
JAVASCRIPT
import { View, Text, Image, StyleSheet } from 'react-native';
import React from 'react';
type UserListItemProps = {
user: any,
};
const UserListItem = ({ user }: UserListItemProps) => {
return (
<View style={styles.container}>
<Image source={{ uri: user.avatarUrl }} style={styles.image} />
<Text style={styles.name}>{user.displayName}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
padding: 5,
margin: 5,
marginHorizontal: 10,
backgroundColor: 'white',
borderRadius: 5,
},
image: {
width: 50,
aspectRatio: 1,
borderRadius: 50,
},
name: {
fontWeight: 'bold',
marginLeft: 10,
},
});
export default UserListItem;
  • Create a new Screen screens/UsersScreen.tsx
JAVASCRIPT
import users from '../assets/data/users.json';
import { FlatList } from 'react-native';
import UserListItem from '../components/UserListItem';
const UsersScreen = () => {
return (
<FlatList
data={users}
renderItem={({ item }) => <UserListItem user={item} />}
/>
);
};
export default UsersScreen;
  • Add the screen as a Modal in the navigation/index.ts
  • Link the header button from the TabOneScreen to open the Users modal

My Account

Let’s use screens/TabTwoScreen as our Profile Page.

JAVASCRIPT
import { StyleSheet, Image } from "react-native";
import { Text, View } from "../components/Themed";
import CustomButton from "../components/CustomButton";
import users from "../assets/data/users.json";
const user = users[0];
export default function TabTwoScreen() {
return (
<View style={styles.container}>
<Image source={{ uri: user?.avatarUrl }} style={styles.avatar} />
<Text style={styles.name}>{user?.displayName}</Text>
<View style={{ marginTop: "auto" }}>
<CustomButton
onPress={() => {}}
text="Sign out"
type="TERTIARY"
fgColor="crimson"
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 10,
alignItems: "center",
},
avatar: {
width: 100,
aspectRatio: 1,
borderRadius: 50,
},
name: {
fontWeight: "bold",
fontSize: 22,
marginVertical: 15,
color: "dimgray",
},
});

Authentication Screens

For the authentication screens, we will re-use the Auth Screens that we have build in this playlist.

  • Install react-hook-form
JAVASCRIPT
npx expo install react-hook-form
  • From the Asset Bundle, add AuthScreens inside our screens folder
  • Add the SignIn and SignOut screens in our RootNavigator
JAVASCRIPT
const isAuthenticated = false;
if (!isAuthenticated) {
return (
<Stack.Navigator>
<Stack.Screen
name="SignIn"
component={SignInScreen}
options={{ title: "Sign in", headerShown: false }}
/>
<Stack.Screen
name="SignUp"
component={SignUpScreen}
options={{ title: "Create an account" }}
/>
</Stack.Navigator>
);
}
return (...)

Next steps

Check out the next part of this project, where we implement the backend of this application using Nhost

✅  Authentications
✅  Database & GraphQL API
✅  Storage
✅  Apollo Client

Watch it live on youtube:


Vadim Savin profile picture

Vadim Savin

Hi 👋 Let me introduce myself

I started my career as a Fullstack Developer when I was 16 y.o.

In search of more freedom, I transitioned to freelancing, which quickly grew into a global software development agency 🔥

Because that was not challenging enough, I started my startup which is used by over 20k users. This experience gave another meaning to being a (notJust) developer 🚀

I am also a proud ex-Amazon SDE and Certified AWS Architect, Developer and SysOps. You are in good hands 👌


Read next