In this tutorial, we will be diving into building Uber 2.0 using React Native. This highly anticipated project will cover iOS and Android development, incorporating technologies like Tailwind and Redux. One of the exciting features we will be implementing is Google Places auto-complete, allowing users to easily search for and select their desired destination. Get ready to learn and create an amazing app, and don't forget to share your excitement and where you're watching from. Let's embark on this journey to build an incredible React Native app together!
"
});
import React from "react";
import { View, Text, StyleSheet } from "react-native";
// 1. Set up redux
export default function App() {
return (
<View style={styles.container}>
<Text>Let's build UBER!</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});
In this tutorial, we're building Uber 2.0 using React Native, covering both iOS and Android development with technologies like Tailwind and Redux. One key feature is Google Places auto-complete, which allows users to search and select their desired destination easily. The app includes a fully interactive map with navigation from point A to point B, clickable points, and automatic resizing. It utilizes Google Distance Matrix API to calculate distances, travel time, and pricing for different options. The app even allows users to click on each option, providing the actual travel time for the selected route. This powerful and highly anticipated project is sure to excite developers and users alike, with many of you already sharing your excitement and locations, such as Manchester, Basingstoke, and Milton Keynes. Let's continue on this journey to build an incredible React Native app together!
In this tutorial, we continue to build the Uber 2.0 React Native app by adding more functionality, such as interactive navigation and dynamic resizing. One example is swiping back to return to the top and start the whole process again. For instance, you can search for a route from the London Eye to Germany, and the app will provide the cost, distance, and travel time.
The app is fully responsive, allowing you to interact with the map, and it even adjusts when the keyboard is opened, pushing the entire screen up. This level of detail and functionality has generated a lot of excitement among developers, so get ready to dive in and expand your React Native skills as we continue to build this incredible app together.
As we break down the build process for the Uber 2.0 clone, it's important to note that we're using React Native. If you're familiar with React, you'll be well-prepared to tackle this project. So don't worry, and feel confident to take the plunge and expand your React Native skills as we create this incredible app together.
Master React Native and Build Feature-Rich Uber 2.0 and Airbnb Apps with Exciting Challenges and Prizes
In this tutorial, we'll not only cover cool React concepts, but we'll also introduce you to React Native, allowing you to create your own iOS and Android apps. This exciting and fun journey will help you expand your React Native skills and build an amazing app together. So gear up for an engaging and hyped experience!
In this tutorial, we'll be leveraging multiple Google APIs, such as the Directions API, Places API, and Distance Matrix API. These APIs enable us to calculate travel times, distances, and much more. Get ready to explore the full potential of these powerful tools as we continue to develop our feature-rich Uber 2.0 clone using React Native.
In addition to the progress we've made on the Uber 2.0 clone, I've updated the playlist for easy access. Don't forget to sign up for the newsletter below to stay updated on the project. But remember, our journey doesn't stop here. We have a lot more in store as we continue to develop this amazing app using React Native, so stay tuned for more exciting features and enhancements!
In this tutorial, we face a massive challenge as we build both Uber 2.0 and Airbnb using React Native. We'll be using various technologies and libraries, such as Tailwind CSS, Redux, React Native Elements, and React Navigation. You'll learn how to use Tailwind CSS in React Native and work with Redux, a popular requirement for many jobs. We'll also dive into using React Native Elements and React Navigation for smoother screen transitions.
Additionally, we'll be incorporating React Native Maps for iOS and Android, pushing the boundaries and making this build accessible to beginners. We have a lot to look forward to as we explore various hooks, practices, and tools needed to develop these incredible apps. To make things even more exciting, there's a 5-day challenge coming up, starting next Tuesday, where we'll cover Airbnb and teach you how to use Mapbox with normal React.
As a bonus, signing up for the challenge will give you access to a free book valued at £50 and tons of prizes. If you're new to React, don't worry! There's a free Skillshare 101 Basics React class available via the second link in the description. So, grab your water and coffee, and let's jump into building these amazing apps together!
In this tutorial, we face the exciting challenge of building the Uber 2.0 and Airbnb apps using React Native. We'll employ various technologies and libraries, such as Tailwind CSS, Redux, React Native Elements, and React Navigation. You'll learn how to use Tailwind CSS in React Native, work with Redux, and explore React Native Elements and React Navigation for seamless screen transitions.
We'll also incorporate React Native Maps for both iOS and Android, making this build accessible even to beginners. As we proceed, we'll delve into various hooks, practices, and tools necessary for developing these incredible apps. On top of that, a 5-day challenge is coming up, which will cover Airbnb and teach you how to use Mapbox with normal React.
Additionally, signing up for the challenge grants you access to a free book valued at £50 and numerous prizes. If you're new to React, don't worry! A free Skillshare 101 Basics React class is available via the second link in the description. So, grab your water and coffee, and let's dive into building these fantastic apps together!
In this tutorial, we'll be using Expo, a highly recommended tool for React Native development. Expo allows you to use your iPhone or Android as a test device, making it an incredibly versatile and powerful tool for developers. As we continue building our app, remember that it's designed to work on both iOS and Android devices. Many of you are enthusiastic and ready to embark on this journey, with shoutouts to Gauraro Gerard, Arjun Joshi, Aldo, Luke, and ShopsRite. Let's jump in and create this fantastic app together!
In this tutorial, we'll begin by setting up the development environment using Expo, a highly recommended tool for React Native development. To start, make sure you have React Native installed and set up on your machine. If you're new to React, you can strengthen your fundamentals in HTML, CSS, and JavaScript before diving into React Native development.
First, install the Expo CLI globally on your machine by running npm install -g expo-cli
. This command allows you to use Expo commands in your terminal. After installation, close and reopen your terminal to ensure the changes take effect.
Next, create a new Expo project by running expo init [project-name]
. In this case, we'll use expo init UberCloneYT
. This command initiates a new Expo project in a directory with the specified name. Feel free to follow along and code with us as we proceed with building the Uber 2.0 clone using React Native.
Now, press the Enter key to initialize the new Expo project. This step will create a new directory with the specified project name and set up the necessary files and folders for your React Native app. As we continue developing the Uber 2.0 clone, you can follow along and code with us to enhance your understanding and skills in React Native.
In this tutorial, we'll continue setting up the development environment for the Uber 2.0 clone using Expo. When you initialize an Expo app, you might need to upgrade your Expo version. Don't worry, upgrading is straightforward with the required command. After initializing the app, you'll be presented with two workflow options: "managed workflow" and "bare workflow." Choose the one that best suits your development needs as you proceed with building the Uber 2.0 clone using React Native.
import React from "react";
import { View, Text } from "react-native";
import { Provider } from "react-redux";
// Set up redux
export default function App() {
return (
<Provider>
<View style>
<Text>Let's build UBER!</Text>
</View>
</Provider>
);
}
In this tutorial, we'll start with a blank managed workflow in the Expo ecosystem. Expo offers numerous benefits, such as the ability to easily test your app on iOS and Android devices. Before Expo, developing for iOS was challenging without a Mac. We'll soon be introducing TypeScript on the channel, but for now, we'll stick with the blank managed workflow. This choice provides a solid foundation for building the Uber 2.0 clone using React Native.
In the past, developing for iOS was challenging without a Mac, as you needed Xcode. However, Expo has made it much easier to develop and test your app on both iOS and Android devices. With Expo, you can simply scan a QR code with your iPhone, and the app loads up on your device. Another advantage of Expo is that it simplifies the process of installing dependencies. While React Native CLI is an option, we recommend using Expo for React Native development until you need to use React Native CLI features. Trust us, it works and makes the development process more convenient.
In this tutorial, we'll be introducing the Redux toolkit into our application, a popular state management library for React Native. To get started, head over to the Redux toolkit website and click on the "Get Started" button. This will provide you with the instructions on how to install the Redux toolkit into your app. If you're interested in learning more about the Redux toolkit and its benefits, you can explore the documentation available on the website. Remember that understanding state management is an essential skill for any React Native developer, so take this opportunity to familiarize yourself with Redux and enhance your app-building abilities.
With Redux Toolkit installed, let's set up Redux for our application. Create a new file called store.js
to store our global data layer. Inside, import configureStore
from the Redux Toolkit and navReducer
from our soon-to-be-created navSlice
file. The store will be set up with an initial state that includes origin
, destination
, and travelTimeInformation
. All three will initially be set to null
.
Create a new folder called slices
and inside that folder, create a file called navSlice.js
. Import createSlice
from Redux Toolkit and define the initial state as an object with origin
, destination
, and travelTimeInformation
properties. Then, create the slice by calling createSlice
and passing in an object with name
, initialState
, and reducers
. The reducers will be responsible for updating the state when actions are dispatched. Define three reducers for setOrigin
, setDestination
, and setTravelTimeInformation
.
Next, export the actions so that they can be used in other parts of the application. Also, create selectors for each item in the initial state. Selectors are used to access the current state of the data layer. Finally, export the navSlice.reducer
, as it will be connected to the store.
Now, in store.js
, import the navSlice.reducer
as navReducer
. In the App.js
file, import the Provider
from react-redux
and wrap the entire application with the Provider
, passing in the store as a prop. With this configuration, Redux is now set up and integrated into the application. As you continue building, you'll be able to dispatch actions to update the state and use selectors to access the current state of the data layer.
With the development environment set up and Redux integrated into our application, you'll find that the app works exactly as intended. It looks great, functions smoothly, and provides an excellent foundation for building the Uber 2.0 clone using React Native. As we continue through this tutorial, you'll gain valuable hands-on experience and deepen your understanding of React Native, ultimately enhancing your app-building skills.
Now, modify the search component by setting the minimum length for search queries to 2. This way, it will require at least two characters for the search to return results. For example, queries like "UK" will be valid. Additionally, you might notice that the search component currently displays "powered by Google." If you prefer to remove this text, simply set the enablePoweredByContainer
attribute to false
. This will hide the "powered by Google" message from the search component.
By setting the enablePoweredByContainer
attribute to false
, you effectively hide the "powered by Google" message, giving a cleaner and more white-labeled appearance to the search component. You're making great progress, and your energy is amazing! Now, when you interact with the search component, you can see that it fetches information as expected.
When handling the onPress
event, you will receive an arrow function that provides two pieces of information: data
and details
. To ensure you receive the details, you need to set a default value of null
and fetch the details using fetchDetails
. Details include crucial information such as geometry locations and coordinates, which you will need to store and render on the map later. To store these details, dispatch an action that goes into the data layer, which is Redux in this case. Then, use a selector that you set up earlier to retrieve the information from the data layer (Redux) and utilize it within the component. This approach ensures seamless interaction with the search component and the ability to fetch and display the necessary information.
Now, to demonstrate that the search component fetches relevant information, you can add console.log(data)
and console.log(details)
within the onPress
event handler. Save your changes, and open the console with the shortcut Command + J
. Next, search for a location, such as "London Bridge", and click on it. The console will display the data and details retrieved from the Google API, including information about the location, such as "one of the best places in the world" and "a beautiful bridge at River Thames". This confirms that the search component is functioning as expected and fetching the necessary details from the Google API.
With the search component functioning correctly and fetching the necessary details from the Google API, let's examine the data and details we receive. Upon inspecting the logged data, you'll find an abundance of information. For example, the geometry
object contains the location
with longitude
and latitude
properties. These coordinates are essential for rendering locations on the map later in the development process. By knowing which data to extract from the API response, you can streamline your efforts and efficiently utilize the information to enhance your app's functionality.
Now that we have the information coming through from the Google Places API, we can proceed with the development of our React Native app. This hands-on learning experience will help you further understand React Native and improve your app-building skills. By successfully integrating the Google Places API, we're making great progress and moving forward with our Uber 2.0 clone.
To improve the user experience, let's add a feature that triggers the search when the return key is pressed. To bring up the keyboard in the emulator, use the shortcut Command + K
. Now, when you select an option, such as "London Bridge," the app should set the origin to the location you pressed. This enhancement will make it more convenient for users to interact with the search component and select their desired locations.
To push information into the data layer that we set up earlier, we need to use a dispatch action. To accomplish this, we'll create a variable called dispatch
. This variable will be used to dispatch actions that update the state in our Redux store, allowing us to interact with the data layer and manage the information within our application.
To interact with the data layer and manipulate the information within our application, we need to dispatch actions. Think of dispatch as a gun; it shoots the action into the data layer along with the payload containing the information we want to change or manipulate. To create a dispatch variable, use the useDispatch
hook from react-redux
:
const dispatch = useDispatch();
With the dispatch variable, we can now import and use the setDestination
and setOrigin
actions from our navSlice
:
import { setDestination, setOrigin } from './slices/navSlice';
By doing this, we'll be able to effectively dispatch these actions and update the state in our Redux store. This will help us manage the information within our application and get it working as intended.
To push information into the Redux store, we use the dispatch
method, which can be visualized as a gun that shoots the action into the data layer along with the payload containing the information we want to change or manipulate. In this case, the action is setOrigin
and the payload is an object. This object will store two things for the origin: the location and some information from the API response, specifically the geometry.location
.
To dispatch the setOrigin
action with the appropriate payload, use the following code:
dispatch(setOrigin({
location: details.geometry.location,
// ... other properties as needed
}));
By doing this, we effectively update the state in our Redux store, allowing us to manage the information within our application and get it working as intended. This hands-on learning experience further deepens your understanding of Redux and React Native, improving your app-building skills.
To store the necessary information from the Google API response in the Redux store, you'll need to access the details.geometry.location
object, which contains the latitude and longitude. This will be stored in the Redux store and can be accessed later in a different component, such as the map component. Additionally, you'll want to store a description of the location, which can be useful for displaying on the screen. To do this, use data.description
. By storing the geometry location and description, you can efficiently utilize the information to enhance your app's functionality.
With the search component functioning correctly and fetching the necessary details from the Google API, let's ensure that the destination is set to null
by default. This is particularly important when navigating between screens to avoid retaining any previous destination information. To set the destination as null
, you can dispatch the setDestination
action with a null
value:
dispatch(setDestination(null));
Now, the origin and destination are set correctly. When you navigate between screens, the destination will be cleared, ensuring a clean slate for the user to select a new location. This improvement contributes to a more seamless user experience within your React Native app.
While testing the search component, an error occurred stating that navSetSetOrigin
is not a function. This indicates that there might be an issue with the dispatch method or the imported action from the navSlice
. Double-check the import statement and ensure that the action is properly defined in the navSlice
. By fixing this issue, you can continue with the development and testing of your React Native app, ensuring that the search component fetches the necessary details from the Google API and interacts seamlessly with the Redux data layer.
In order to fix the error stating that navSetSetOrigin
is not a function, make sure to double-check the import statement for the setOrigin
and setDestination
actions and confirm that they're correctly defined in the navSlice
. After verifying that the navSlice
is properly set up and the actions are being exported, test the search component again by searching for a location like "London Bridge." This will help ensure that the search component is functioning correctly, fetching the necessary details from the Google API, and interacting seamlessly with the Redux data layer.
Sometimes, React Native can behave unexpectedly or seem a bit unusual. In such cases, simply refreshing the application might resolve the issue. As we saw previously, everything was set up correctly, and a refresh was all that was needed. So, if you encounter any odd behavior in your React Native app, consider trying a refresh before diving into more in-depth debugging.
Sometimes, simply refreshing your app can resolve unexpected issues or unusual behavior. For instance, with the search component correctly set up and integrated with the Redux data layer, simply reloading the app might fix any issues. Now, when you click "Get a ride" and search for a location like "London, UK," the information is properly stored in Redux. So, if you encounter any odd behavior in your React Native app, consider trying a refresh before diving into more in-depth debugging.
Now, let's move on to rendering a map on the "Get a ride" screen. Head over to the map screen, where we've already set up the origin information. This will involve integrating the map component into your React Native app, which is an exciting step in the development process. Remember to like, subscribe, and sign up for the Airbnb challenge if you're eager to continue learning. With the map component set up, your app will be one step closer to functioning like a fully-fledged Uber 2.0 clone.
In the map screen, we'll have two primary sections. The first section will be the actual map, which will take up half of the screen. The other half will be dedicated to displaying details in a separate view. By dividing the screen into two distinct sections, you can effectively present both the map and the relevant information in your React Native app. This layout will allow for a more organized and visually appealing user interface.
We'll create two separate views within our map screen to prepare for displaying the map and additional information. Each view will take up half of the screen using Tailwind CSS. To achieve this, we can use Emmet to quickly generate the views:
<View className="h-1/2"></View>
<View className="h-1/2"></View>
Make sure to import Tailwind CSS in your map screen component. This layout will provide an equal distribution of space to both views, leading to a visually appealing and organized user interface. And that's it! You've successfully set up your screen to display both the map and relevant information for your React Native app.
I hope you had a good week, and I'm glad to hear that you're having a good week too. Now that we've set up the map screen with two separate views, we can test it by clicking on the "Get a ride" button. This should take us to the map screen where we can see the two views we created. Great, now we're on the screen, and everything looks good so far!
To create the map view on the "Get a ride" screen, we won't use a SafeAreaView
because we want the map to extend into the "dangerous area" for a more immersive experience. So, let's focus on implementing the map within the first half of the screen that we previously set up.
To create a separate map component, first, create a new file called map.js
. In this file, use the React Native Functional Export with StyleSheet to generate a functional component. This approach helps keep the logic separate and organized, which is crucial for more complex projects. Once you've created the map component, you can add your desired content or functionality, such as displaying the map view in your React Native app. This type of component organization makes it easier to maintain and modify your application as needed.
// map.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
const Map = () => {
return (
<View>
<Text>I am a map</Text>
</View>
);
};
const styles = StyleSheet.create({});
export default Map;
Now you have successfully created a separate map component to handle the map display and related logic in your React Native application.
Now that the map screen is set up with two separate views, let's render the map component in the first view. To do this, import the Map
component and replace the placeholder text with <Map />
. After making this change, you should no longer see the "I am a map" text in the top half of the screen.
Next, let's focus on making the map look nice. Since the map extends into the "dangerous area," we'll need to figure out how to adjust its appearance to create a more immersive user experience.
To integrate the map view using React Native Maps, first, install the package by running the following command:
yarn add react-native-maps
Once the package is installed, you can import MapView
from react-native-maps
:
import MapView from 'react-native-maps';
Now you're ready to use the MapView
component to display the map view in your React Native app. Refer to the React Native Maps GitHub repo for more information and examples.
Now that we have set up the map screen with two separate views, it's time to render the map component in the first view. Start by importing the Map
component and replacing the placeholder text with <Map />
. This change will remove the "I am a map" text from the top half of the screen.
Next, let's make the map look visually appealing. Since the map extends into the "dangerous area," we need to adjust its appearance for an immersive user experience. To integrate the map view using React Native Maps, install the package with:
yarn add react-native-maps
After installation, import MapView
from react-native-maps
:
import MapView from 'react-native-maps';
You are now ready to use the MapView
component to display the map view in your React Native app. For more information and examples, refer to the React Native Maps GitHub repo. Enjoy the progress of your app build and keep up the great work!
To render the map view on the screen, let's use an example from the React Native Maps GitHub repo as a starting point. Copy the example, and we'll modify it to fit our requirements. Replace the entire map component with the MapView
component and import both MapView
and Marker
. The Marker
will be used later for adding a pin to the map.
import MapView, { Marker } from 'react-native-maps';
After saving your changes, you might notice that the map is not visible. This is because the MapView
component needs some styling. Let's add the necessary styles to make the map view visible on the screen.
To make the map visible, add a Tailwind style to the MapView
component by setting style
equal to flex-1
. This will ensure that the map uses as much room as it can within its half-container, which means it should take up half the screen. Don't forget to import Tailwind CSS in your map component. After saving your changes and clicking on the "Get a ride" button, you should see the map view displaying San Francisco. It's starting to look great! As you progress, the origin will be pulled through and displayed on the map. Keep up the good work and continue refining your React Native app!
When you click the "Get a ride" button, the goal is to display the origin location on the map. For example, if you enter "London, UK" and click "Get a ride," the map should show London. To achieve this, you will need to pass the origin data to the map component and use it to update the map view accordingly. Keep up the good work and continue refining your React Native app!
Thank you for the kind words and support! It's great to see developers from around the world coming together and learning from each other. Your progress in building your React Native app is impressive, and knowing that this content has been helpful to you is truly rewarding. Keep up the excellent work, and enjoy your journey in the world of programming! Best regards from the entire community.
To enhance the user experience when navigating, it's important to focus on displaying essential information on the map. The default map view may be cluttered with unnecessary details. To address this, you can change the mapType
attribute to mutedStandard
, which simplifies the map appearance. This change highlights the actual navigation, making it easier for users to follow. Here's how you can modify the map view in your React Native app:
<MapView
mapType="mutedStandard"
style={styles.map}
...
/>
By using the mutedStandard
map type, you provide users with a cleaner and more focused navigation experience. Keep refining your app and enhancing the user experience!
To display the origin location on the map, you'll need to access the data from the global store. Using the useSelector
hook from Redux, you can fetch the origin data. First, import useSelector
and the selectOrigin
selector from your navSlice
:
import { useSelector } from 'react-redux';
import { selectOrigin } from './navSlice';
Next, use the useSelector
hook to retrieve the origin data:
const origin = useSelector(selectOrigin);
This code will access the global store's state, navigate to the nav
property and retrieve the value of origin
. Now, you can utilize this data to display the origin location on your map.
Now that you have access to the origin data, you can use it to set the initial region of the map. Since the origin is an object, you can access its latitude and longitude properties directly. Update the MapView
component's initialRegion
attribute as follows:
initialRegion={{
latitude: origin.location.latitude,
longitude: origin.location.longitude,
...
}}
After saving your changes, the map should now display the specified origin, such as London. Configuring the latitude and longitude deltas can be challenging when starting off, but with practice, you'll soon master this aspect of map customization. Continue refining your React Native app and enjoy the progress!
With the initial region set, you may wonder about the latitude and longitude deltas. Deltas determine the zoom level on the map. Without delving too much into the technical details, a good starting value for street-level distance is typically used. Don't worry, this is the only time you'll need to modify the delta values – once you've set them, managing the rest of the map customization will be much easier. Keep up the great work as you continue developing your React Native app!
Instead of calculating the delta values manually, which can be a headache, there's a simple trick that can save you time and effort. Don't worry about what a delta is or how it's calculated – the important thing is to focus on building and improving your React Native app.
A convenient solution to avoid calculating delta values manually is available, saving you time and effort. For instance, if you input "Scotland, UK" and click "Get a ride," the map will display Scotland without the need for manual delta calculations. This technique simplifies the process of managing map customization and allows you to focus on building and improving your React Native app.
After successfully displaying the map with the origin location, you can now test it by entering different locations. For example, type in "Los Angeles, California, USA" and click "Get a ride." You should see the map update to show Los Angeles. Feel free to experiment with various locations to ensure that the map updates accordingly. This is a great way to verify that your React Native app is working as intended, displaying the correct locations on the map based on user input. Keep up the excellent work!
Now, let's add a marker to the map at the origin location, like London Bridge. To do this, simply place a Marker
component within the MapView
component, and set its coordinate
attribute to the origin's latitude and longitude. Once you've done this, when you search for a location such as "London Bridge" and click "Get a ride," a marker will appear on the map at that location. This feature provides users with a clear visual indication of their chosen origin. Keep experimenting with different locations and improving your React Native app, and don't forget to thank the supportive community for their encouragement!
To display a marker on the map at the origin location, you need to modify the MapView
component so that it is no longer self-contained and can have children. Inside the MapView
, you can add a Marker
component. However, it's important to only render the marker when the origin is available. Here's how you can achieve this:
<MapView ...>
{origin && (
<Marker
coordinate={{
latitude: origin.location.lat,
longitude: origin.location.lng,
}}
/>
)}
</MapView>
This code snippet checks if the origin exists before rendering the Marker
component with the appropriate latitude and longitude coordinates. Now, when you search for a location and click "Get a ride," a marker will appear on the map at the specified origin.
To display a marker on the map at the origin location, you'll need to utilize the Marker
component within the MapView
component. When the origin is available, the marker should be rendered on the map.
Make sure to use optional chaining to avoid errors when the origin is undefined:
{origin && (
<Marker
coordinate={{
latitude: origin.location.lat,
longitude: origin.location.lng,
}}
/>
)}
This code snippet checks if the origin exists before rendering the Marker
component with the appropriate latitude and longitude coordinates. Now, when you search for a location and click "Get a ride," a marker will appear on the map at the specified origin. The pin drop feature provides users with a clear visual indication of their chosen location. Plus, your app will support zooming in and out, which further enhances the user experience. Keep experimenting with different locations and refining your React Native app!
To enhance the marker's appearance and provide more information, you can add a title, description, and identifier to the Marker
component. Simply include these properties within the component and set their values as desired:
<Marker
coordinate={...}
title="Origin"
description="London Bridge, London UK"
identifier="origin"
/>
After saving your changes, clicking on the marker will display the title "Origin," the description "London Bridge, London UK," and an identifier. You can customize these properties to match your preferences. While this example doesn't include a live location feature, you can easily implement it by following the documentation. With these enhancements, your React Native app offers an improved user experience that includes informative markers and a fully functional map.
By now, you might want to hide the navigation options if the origin hasn't been entered. For example, you don't want to go to the map screen if the user hasn't specified an origin like "Facebook headquarters." To achieve this, go to your navOptions
and make some updates. You'll want to make the navigation option unclickable when the origin details are not filled out. This way, your React Native app ensures that users have provided an origin before allowing them to navigate to the map screen, enhancing the overall user experience.
To disable the navigation option when the origin is not specified, you can use the TouchableOpacity
component and set its disabled
attribute based on the presence of the origin. First, you need to pull the origin using the useSelector
hook:
const origin = useSelector(selectOrigin);
Then, set the disabled
attribute of the TouchableOpacity
component to true if there is no origin:
<TouchableOpacity disabled={!origin} ...>
To visually indicate that the navigation option is disabled, you can adjust the opacity of the container View
using the tailwind
utility. Add a JSX tag inside the style
attribute and conditionally apply the opacity-20
class if there is no origin:
<View style={tailwind(!origin && 'opacity-20')} ...>
Now, the navigation option will appear less prominent when no origin is provided, enhancing the overall user experience of your React Native app.
After refreshing the app, you can now test the updated navigation options. For instance, when you input "London," the button becomes clickable, allowing you to navigate to the map screen and display London. To further demonstrate its functionality, you can try searching for "One Hacker Way," which brings up the Facebook headquarters in Menlo Park. With these improvements, your React Native app provides a better user experience by ensuring that the navigation option is only clickable once an origin has been entered. Go ahead and test different locations to make sure everything works smoothly. And don't forget, all timestamps for this tutorial will be available in the description. Keep experimenting and refining your app!
Continuing with the development of your React Native app, it's time to implement the bottom section. Head back to your map screen and start working on the remaining parts. As you progress, remember to make use of the free, complete access to the tutorial video, which will be available in a couple of hours. This resource will prove invaluable when reviewing your work. Keep pushing forward and refining your app to create an excellent user experience.
At this point, you have made significant progress on your map screen, and it seems to be functioning well. Now is an excellent time to review your work and make any necessary adjustments. Remember, as you continue to develop your React Native app, strive to create a great user experience by refining and improving each feature. Keep up the good work!
To build the bottom section on the map screen, you can utilize a nested stack navigator that occupies the remaining half of the screen. This nested stack navigator can have its own set of swipable screens, allowing users to navigate back and forth between these screens seamlessly. As you work on this part of your React Native app, keep in mind the importance of creating an intuitive and user-friendly interface. Implementing a nested stack navigator within the map screen will provide users with an enhanced experience, making it easier for them to interact with your app.
To create a nested stack navigator for the bottom section of the map screen, you'll first need to define a new stack navigator within the map screen component. Declare a constant stack
and set it equal to the invocation of createStackNavigator
:
const stack = createStackNavigator();
This new stack navigator will function as a lower-level navigator within the map screen, similar to the main one in your app.js
. Now, you can place this nested stack navigator inside your map screen component using the stack.Navigator
element. This will allow users to seamlessly navigate between swipable screens within the bottom section, enhancing their experience and interaction with your React Native app.
To implement the bottom section on the map screen, you'll create a stack navigator with two screens. The first screen will be the "Navigate Card," which prompts users to enter their desired destination. After entering the destination details, users will proceed to the second screen, the "Ride Options Card." This screen will display various ride options like UberX, UberXL, and UberLuxe for users to choose from. By incorporating this nested stack navigator, you'll provide a seamless and user-friendly interface for your React Native app.
Now, go ahead and create a stack.Screen
for the "Navigate Card," which is a component you'll need to add:
<stack.Screen name="NavigateCard" component={NavigateCard} />
At the moment, the NavigateCard
component doesn't exist, so you'll need to create it. Once you've implemented it, the nested stack navigator will include the "Navigate Card" as one of its screens, allowing users to interact with it within the bottom section of the map screen in your React Native app.
Create a new file called NavigateCard.js
and generate a React Native functional component using the Sashi snippet. Type yo
in the component and save the file. Now, head back to the MapScreen
component and import the NavigateCard
component by adding the following import statement:
import NavigateCard from './NavigateCard';
With this import, you should see "yo" displayed on the screen, indicating that the NavigateCard
component has been successfully added to the nested stack navigator within the map screen. This is the first step towards creating a seamless and user-friendly interface for your React Native app.
Now, you'll create another screen for the nested stack navigator on the map screen, called "RideOptionsCard." This screen will allow users to choose their preferred ride type, such as UberX, UberXL, or UberLuxe. First, create a new file named RideOptionsCard.js
and generate a React Native functional component using the Sashi snippet. Inside the component, add the message "Pick a ride" to provide some context:
// RideOptionsCard.js
...
return (
<View>
<Text>Pick a ride</Text>
</View>
);
...
Next, head back to the MapScreen
component and import the RideOptionsCard
component:
import RideOptionsCard from './RideOptionsCard';
Now, add a stack.Screen
for the "RideOptionsCard" component to the nested stack navigator:
<stack.Screen name="RideOptionsCard" component={RideOptionsCard} />
With these additions, you should see "Pick a ride" displayed on the screen when navigating to the second screen within the bottom section. This confirms that the RideOptionsCard
component has been successfully added to the nested stack navigator on the map screen, further enhancing the user experience of your React Native app.
With your React Native app progressing well, it's essential to keep refining and improving each feature to create an excellent user experience. Keep in mind the importance of an intuitive and user-friendly interface as you work on your app. As you continue with the development, remember to make use of the free, complete access to the tutorial video, which will be available in a couple of hours. This resource will prove invaluable when reviewing your work. And don't forget to subscribe and hit the like button if you're enjoying the content, as it helps support the channel and ensures more frequent updates. Keep pushing forward and refining your app to create a fantastic user experience!
Now let's focus on building the actual "Navigate Card" component. In the NavigateCard.js
file, you'll need to set up a few things for the card to function correctly. This component will prompt users to input their desired destination, so be sure to create an intuitive and user-friendly layout. As you continue working on your React Native app, always strive to refine and improve each feature to provide an excellent user experience for your users.
To ensure that the "Navigate Card" component doesn't overlap with the unsafe area at the bottom of the screen, wrap it inside a SafeAreaView
. This will keep the component above the unsafe area and preserve its appearance. To do this, import the SafeAreaView
from the React Native library:
import { SafeAreaView } from 'react-native';
Next, replace the View
element in the NavigateCard.js
component with a SafeAreaView
:
return (
<SafeAreaView>
...
</SafeAreaView>
);
Now, the "Navigate Card" component will be safely positioned above the unsafe area at the bottom of the screen, maintaining a user-friendly layout. Additionally, you may want to change the background color of the "Navigate Card" to a gray color, providing a more visually appealing design for your users. Keep refining and improving your app's features to provide an excellent user experience. And remember, the support and engagement of your audience are what make your development journey possible, so don't forget to show your appreciation for their contributions.
To style the "Navigate Card" component with a white background, you can use the Tailwind CSS library. Add the tailwind
style to the SafeAreaView
in the NavigateCard.js
component as follows:
<SafeAreaView style={tailwind('flex-1 bg-white')}>
...
</SafeAreaView>
This will give the "Navigate Card" a white background and ensure that it occupies the entire available space within the SafeAreaView
. Using Tailwind CSS provides a convenient and efficient way to style your components, enhancing the overall appearance and user experience of your React Native app.
To style the "Navigate Card" component with a white background, you can use the Tailwind CSS library. Add the tailwind
style to the SafeAreaView
in the NavigateCard.js
component as follows:
<SafeAreaView style={tailwind('bg-white flex-1')}>
...
</SafeAreaView>
This will give the "Navigate Card" a white background and ensure that it occupies the entire available space within the SafeAreaView
. Using Tailwind CSS provides a convenient and efficient way to style your components, enhancing the overall appearance and user experience of your React Native app.
Now, let's add a "Good morning" message to the "Navigate Card" component. In Uber's app, there is a personalized greeting message, so we'll include one in our app, too. To achieve this, insert a Text
element with the desired message, like so:
<SafeAreaView style={tailwind('bg-white flex-1')}>
<Text>Good morning, Sonny</Text>
...
</SafeAreaView>
With these changes, your "Navigate Card" component now has a white background and a friendly greeting message, making it more visually appealing and providing a better user experience.
To personalize the "Navigate Card" component, we will add a greeting message. Although our app doesn't currently have login functionality, you can always upgrade it later to include user-specific greetings. For demonstration purposes, we will use a fixed name in the greeting message.
<SafeAreaView style=>
<Text>Good morning, Sonny</Text>
...
</SafeAreaView>
To style the greeting message, center the text, add a 5-unit padding on the top and bottom, and set the text size to extra large using the following Tailwind CSS classes: text-center
, py-5
, and text-xl
.
<Text style={tailwind('text-center py-5 text-xl')}>Good morning, Sonny</Text>
Now, the "Navigate Card" component includes a personalized greeting message, enhancing the user experience. If you enjoy following this tutorial and would like to show your support, consider sharing your progress on social media and tagging the author's Instagram handle, as it helps them see who is benefitting from their content. Happy coding!
After successfully setting up the "Navigate Card" component with a friendly greeting message and white background, you can now find the code on the public GitHub repository. Feel free to grab the code and use it as a reference for your own React Native app. The next step is to add a View
to the component, which will further enhance its appearance and functionality. Keep refining your app's features and continue working towards a seamless and user-friendly interface for your users.
As we continue building the "Navigate Card" component, we'll add another Google Places Autocomplete, similar to the one we previously implemented. This new Autocomplete will be responsible for setting the destination. To begin, let's create a new View
and apply the necessary styles:
...
return (
<SafeAreaView style=>
<Text style=>Good morning, Sonny</Text>
<View>
...
</View>
</SafeAreaView>
);
...
With the new View
in place, you can now add the Google Places Autocomplete to allow users to input their desired destination. Keep refining your app's features and working towards an intuitive and user-friendly interface for your users.
To style the new View
, add a top border with a gray color, and allow it to shrink when necessary. Add the following styles to the View
: border-t
, border-gray-200
, and flex-shrink
.
<View style=>
...
</View>
Inside this View
, create another View
that will contain the Google Places Autocomplete component for setting the destination.
<View style=>
<View>
{/* Google Places Autocomplete goes here */}
</View>
</View>
Now you have a properly styled View
that can host the Google Places Autocomplete component, enabling users to input their desired destination. Keep refining your app's features and working towards an intuitive and user-friendly interface for your users.
To prepare for adding the Google Places Autocomplete component, you'll need to import it along with the Google Maps API key from your environment variables. Update your imports in the NavigateCard.js
file as follows:
import ...
import {GooglePlacesAutocomplete} from '...';
import {GOOGLE_MAPS_APIKEY} from '@env';
Now, within the inner View
you created earlier, render the GooglePlacesAutocomplete
component as a self-closing component:
<View style=>
<View>
<GooglePlacesAutocomplete
...
/>
</View>
</View>
With the Google Places Autocomplete component in place, users can now input their desired destination. Continue refining your app's features and working towards creating an intuitive and user-friendly interface for your users.
Now, let's configure the Google Places Autocomplete component to resemble Uber's user experience. Set the placeholder
attribute to "Where to?" as follows:
<GooglePlacesAutocomplete
placeholder="Where to?"
...
/>
Specify the API key and add a debounce of 400 milliseconds. The debounce ensures that the component doesn't search on every keystroke, but rather waits until the user has stopped typing for 400 milliseconds before initiating a search.
<GooglePlacesAutocomplete
placeholder="Where to?"
apiKey={GOOGLE_MAPS_APIKEY}
debounce={400}
...
/>
With these adjustments, the "Navigate Card" component now includes a destination input field that closely resembles Uber's user experience. Keep refining your app's features and work towards creating a seamless and user-friendly interface for your users.
You're welcome! It's great to hear that these tutorials have been helpful in improving your React Native and React JS portfolios. Now, moving forward with the styling, you can combine both Tailwind CSS and custom styles to achieve the desired look for your app. For instance, let's create styles for the input boxes. You can do this by creating a new style object and applying it to the relevant components:
const inputBoxStyles = {
...
};
// Then, apply the styles to the input boxes:
<View style={inputBoxStyles}>
...
</View>
By combining Tailwind CSS and your own custom styles, you can achieve a unique and visually appealing design for your React Native app. Happy coding!
After successfully configuring the Google Places Autocomplete component, you can further refine its appearance by applying custom styles to its container. You can override certain fields in the container by applying a new style object. For example, set the background color to white, add a padding top of 20 units, and set the flex to zero:
const containerOverride = {
backgroundColor: 'white',
paddingTop: 20,
flex: 0,
};
// Apply the styles to the Google Places Autocomplete container:
<GooglePlacesAutocomplete
...
styles={{
container: containerOverride,
}}
/>
Upon saving your changes, you'll notice a subtle visual improvement, as the container now has a white background and appropriate padding. Keep refining your app's features and working towards creating a seamless and user-friendly interface for your users.
Additionally, there are two more fields, textInput
and textInputContainer
, that you can style to enhance the appearance of the Google Places Autocomplete component. Create custom styles for these fields by changing the background color to a subtle gray, setting the border radius to zero, choosing a font size of 18, and adding some padding around the container. Apply these styles to the Google Places Autocomplete component to achieve a more polished look for your app.
// Create custom styles for textInput and textInputContainer
const textInputStyles = ;
const textInputContainerStyles = ;
// Apply the styles to the Google Places Autocomplete component
<GooglePlacesAutocomplete
...
styles=}
/>
Upon applying these styles, you'll notice an improvement in the appearance of the Google Places Autocomplete component. Keep refining your app's features and working towards creating a seamless and user-friendly interface for your users.
After successfully configuring the Google Places Autocomplete component and refining its appearance with custom styles, you have now learned three different ways to style your components in a React Native app: inline styles, StyleSheet styles, and Tailwind CSS. By combining these various styling methods, you can achieve a unique and visually appealing design for your app. Remember that you can use each of these methods depending on your specific requirements and preferences. Keep refining your app's features and working towards creating a seamless and user-friendly interface for your users. Happy coding!
With the "Navigate Card" component coming along nicely, you can further enhance the Google Places Autocomplete by adjusting its properties. Firstly, enable the fetchDetails
attribute to fetch additional details about the selected destination. Additionally, disable the "Powered by Google" container to create a cleaner look for your app:
<GooglePlacesAutocomplete
...
fetchDetails
suppressDefaultStyles
/>
By applying these changes, you not only enhance the functionality of the component but also create a more polished appearance. Remember that your creativity and flexibility can go a long way in determining how to best utilize the various styling techniques available. Keep refining your app's features and working towards creating a seamless and user-friendly interface for your users.
With the "Navigate Card" component set up, it's time to test it out. As an example, try inputting "London." You may not see any results at first because the query is missing. Recall that the query is where you put in your Google Maps API key and specify the language. So, insert your Google Maps key and try again. Now, when you input "London," you should see results as the component has a valid key for querying the Google APIs. Enjoy experimenting with the Google Places Autocomplete component and continue refining your app's features and user interface.
To enable the search functionality when pressing the return key, configure the Google Places Autocomplete component accordingly. This will improve the user experience, making it more intuitive and convenient for users to search for their desired destination. Remember to continuously test and refine your app's features to ensure a seamless and user-friendly interface. Don't worry if you encounter occasional connection issues while working on your app; just make sure to save your progress frequently and keep pushing forward. Happy coding!
Now, let's handle the onPress
event for the Google Places Autocomplete component. You should use an arrow function that receives two parameters: data
and details
. To protect against potential errors, set default values for both parameters to null
. Here's the code snippet for the onPress
event:
<GooglePlacesAutocomplete
...
onPress={(data, details = null) => {
// Your code here
}}
/>
This setup allows you to handle user interactions with the autocomplete component and take action based on the selected destination. Make sure to keep testing your app and refining its features to ensure a seamless and user-friendly interface.
With the onPress
event configured for the Google Places Autocomplete component, it's time to set the destination. You previously set the origin using dispatch
; now, use the same method to set the destination. Here's the updated code snippet:
<GooglePlacesAutocomplete
...
onPress=}
/>
By following this approach, you can now properly set both the origin and destination. Make sure to keep testing your app and refining its features to ensure a seamless and user-friendly interface.
To set the destination, you first need to access the dispatch object using the useDispatch
hook from React Redux:
const dispatch = useDispatch();
Next, import the setDestination
action from your navigation slice:
import { setDestination } from '...';
With the dispatch object and setDestination
action, you can now update the onPress
event in the Google Places Autocomplete component to set the destination:
<GooglePlacesAutocomplete
...
onPress=}
/>
This allows you to properly set both the origin and destination. Continue testing your app and refining its features to ensure a seamless and user-friendly interface.
Now, to set the destination, dispatch the setDestination
action with a payload containing the location and description. This approach allows you to handle both the origin and destination effectively. Keep refining your app's features and working towards creating a seamless and user-friendly interface for your users. Your dedication and attention to detail throughout this process are truly commendable. Keep up the great work!
After setting the destination, you'll want to navigate to the next screen. To do this, first obtain the navigation
object by using the useNavigation
hook:
const navigation = useNavigation();
Make sure to import useNavigation
from your navigation library. Now, when a user selects an address from the list, you want to navigate them to the "Pick a Ride" screen. To achieve this, add the navigation action within the onPress
event of the Google Places Autocomplete component:
<GooglePlacesAutocomplete
...
onPress={(data, details = null) => {
// Set destination and other logic
navigation.navigate("Pick a Ride");
}}
/>
With this in place, the user will be navigated to the "Pick a Ride" screen upon selecting an address, creating an intuitive and seamless experience. Keep refining your app's features to ensure a user-friendly interface.
Now, to navigate to the "Ride Options Card" screen, use the navigation.navigate
method. Make sure you have the navigation
object by using the useNavigation
hook. Update the onPress
event of the Google Places Autocomplete component accordingly:
<GooglePlacesAutocomplete
...
onPress=}
/>
With this setup, when a user selects an address, they will be navigated to the "Ride Options Card" screen, providing an intuitive and seamless experience. If you encounter any issues, double-check your code and ensure the dispatch
and navigation
objects are set up correctly. Continue refining your app's features to ensure a user-friendly interface.
After refreshing the app, you might encounter a request timeout error. Don't worry, just refresh again and try searching for a location like "London" or "Scotland, UK." Once you see the search results, this means the destination has been successfully pushed into the Redux store, and the app navigates to the next screen. If you want to go back, simply swipe back to return to the previous screen. Keep experimenting with your app and refining its features to ensure a seamless and user-friendly experience for your users.
With the destination functionality in place, your app has gained significant power and versatility. Now that you have a destination, it's time to enhance your map component. Head back to the map component to make further improvements, ensuring that your app continues to progress and provide a seamless and user-friendly experience for your users. Keep up the great work, and remember that each refinement brings you closer to a polished and effective final product.
As you return to the map component, it's time to introduce the directions API to your app. By incorporating directions, you will greatly enhance your app's functionality. To achieve this, you'll need to include a React Native dependency. The directions implementation is quite slick, and integrating it into your app will contribute to a more seamless and user-friendly experience for your users.
To integrate directions into your map component, you'll need to install the react-native-maps-directions
dependency. This will sit inside your MapView
component, just like a marker, and provides directions from point A to B. To install the dependency, run the following command:
yarn add react-native-maps-directions
With this dependency installed, you'll be able to greatly enhance your app's functionality by incorporating directions, contributing to a more seamless and user-friendly experience for your users.
Now you can retrieve the destination from the Redux store, just like you did with the origin. To do this, use the useSelector
hook to access the selectDestination
selector. Don't forget to import it:
const destination = useSelector(selectDestination);
With the destination successfully retrieved, your app now has both the origin and destination information, allowing you to further enhance the map component and provide a seamless and user-friendly experience for your users.
With both the origin and destination retrieved, you can proceed to render the directions on your map. To do this, check if both origin
and destination
exist, and then render the MapViewDirections
component. First, import the component at the top of your file:
import MapViewDirections from 'react-native-maps-directions';
Next, add the MapViewDirections
component within your map, providing it with the origin and destination descriptions:
{
origin &&
destination &&
(
<MapViewDirections
origin={origin.description}
destination={destination.description}
...
/>
)
}
This self-closing component takes a variety of attributes to work properly. With this setup, your app will display directions between the origin and destination points, enhancing the user experience and providing a more seamless interface.
With both the origin and destination in hand, you can now render directions on your map using the MapViewDirections
component. First, set the destination equal to destination.description
, which is the text saved in the store. Then, pass the Google Maps API key from your environment variables:
destination=destination.description
apiKey={process.env.GOOGLE_MAPS_API_KEY}
...
Next, configure the appearance of the directions by setting the strokeWidth
and strokeColor
. In this case, we'll use a black line, similar to Uber's design:
strokeWidth={3}
strokeColor="black"
After saving your changes, you'll see that the directions are displayed on the map, providing a more seamless and user-friendly experience.
Now that you've successfully integrated the MapViewDirections
component and configured its appearance, you can test the directions feature in your app. For example, try setting the origin to Scotland and the destination to London Bridge. You'll see that the app displays a detailed route between the two locations, including roundabouts and streets along the way. This addition of directions greatly enhances your app's functionality, providing a more seamless and user-friendly experience for your users. Keep refining the app and experimenting with different destinations to ensure the directions feature works smoothly.
To solve the problem of zooming out the map when you have both the origin and destination points, we'll implement a smooth method that ensures both points A and B are within the map view. This will provide your users with a more seamless and user-friendly experience when the destination is set.
To reference and manipulate the map, you'll need to use the useRef
hook, which acts as a pointer to a specific object or component. Start by creating a map reference using useRef
and initialize it with a value of null
:
const mapRef = useRef(null);
Next, attach this reference to the MapView
component. This way, you can access and modify the map whenever necessary:
<MapView
ref={mapRef}
...
/>
Now, you'll need to utilize the useEffect
hook to perform actions when specific conditions are met. The useEffect
hook is a core concept in React that allows you to perform side effects in function components.
useEffect(() => {
...
}, []);
Pay close attention to the implementation of the useEffect
hook, as it will help you create a smooth method for adjusting the map view to ensure both origin and destination points are visible, providing a seamless user experience.
With the destination functionality in place, let's improve the map component by introducing the Directions API. Start by installing the react-native-maps-directions
dependency:
yarn add react-native-maps-directions
Retrieve the destination from the Redux store using the useSelector
hook and the selectDestination
selector:
const destination = useSelector(selectDestination);
Import the MapViewDirections
component and render it within your map, providing the origin and destination descriptions:
import MapViewDirections from'react-native-maps-directions';
// Inside your map component
<MapViewDirections
origin=origin.description
destination=destination.description
apiKey=...
strokeWidth=...
strokeColor="black"
/>
To ensure that both origin and destination points are visible on the map, use the useRef
hook to create a map reference and attach it to the MapView
component:
const mapRef = useRef(null);
// Inside your JSX
<MapView
ref=...
...
/>
Next, utilize the useEffect
hook to perform actions when the origin or destination changes:
useEffect(() => {
if (!origin || !destination) return;
// Add logic here to modify the map view
}, [origin, destination]);
With these enhancements, your app will display directions between the origin and destination points, providing a more seamless and user-friendly experience. Test the directions feature with different destinations to ensure it works smoothly.
To zoom and fit the map view to both the origin and destination markers, you can use the fitToSuppliedMarkers
function from the MapView
component. First, access the current map reference by calling mapRef.current
. Then, use the fitToSuppliedMarkers
method and pass an array containing the identifiers of the origin and destination markers as its first argument:
mapRef.current.fitToSuppliedMarkers(['origin', 'destination']);
The origin and destination marker identifiers are the same as the ones you used when creating the markers on the map. By calling this method, the map will automatically adjust its view to ensure both markers are visible, providing a more seamless and user-friendly experience.
Additionally, you can now add the destination marker to the map, similar to how you added the origin marker. Copy the origin marker block and adjust it to use the destination
and update the identifier to 'destination'
. Remember to import the useEffect
hook from React at the top of your file:
import { useEffect } from 'react';
With the markers in place, you can now use the fitToSuppliedMarkers
function from the MapView
component to zoom and fit the map view to both the origin and destination markers. Access the current map reference by calling mapRef.current
and pass an array containing the identifiers of the origin and destination markers as its first argument:
mapRef.current.fitToSuppliedMarkers(['origin', 'destination']);
This method will automatically adjust the map view to ensure both markers are visible, providing a more seamless and user-friendly experience.
You can improve the map view by adding edge padding to provide some space around the markers when the map zooms in. To do this, pass an edgePadding
object to the fitToSuppliedMarkers
method. This object should have properties for top
, left
, bottom
, and right
, all set to a value of 50. This means the map won't zoom in too close to the markers, keeping some padding around them.
mapRef.current.fitToSuppliedMarkers(['origin', 'destination'], {
edgePadding: { top: 50, left: 50, bottom: 50, right: 50 },
});
Now, when you set the origin and destination – for example, London and Essex – the map will display the route with appropriate padding around the markers. The useEffect
hook ensures that the map view will update whenever the origin or destination changes, providing a smooth and user-friendly experience.
With the map view improvements in place, try setting the origin to Birmingham, UK, and you'll see the map automatically adjusts to display both origin and destination markers, along with the directions. The fitToSuppliedMarkers
function simplifies the process, eliminating the need to deal with deltas and other complexities. This enhancement significantly improves the user experience, making the app more seamless and user-friendly. Keep experimenting with different origin and destination points to ensure the map view adjusts correctly and provides a smooth experience for your users.
Continuing on, the map view is looking good and the directions feature is functioning nicely. For instance, if you set the origin to London Eye or London Bridge, the map smoothly navigates to those locations. Similarly, if you try setting the origin to Scotland, UK, the map zooms out gracefully, providing a slick animation. This demonstrates that the enhancements made to the map view are working effectively, offering users a seamless and enjoyable experience. Keep experimenting with different origin and destination points to ensure the map view continues to adapt and provide a smooth experience for your users.
Now, let's enhance the user interface by adding two buttons at the bottom of the screen, similar to the ones you'd find in the Uber app. These buttons will allow users to pick a ride and proceed with the booking process. To create these buttons, you can add UI components to your existing screen layout. With these buttons in place, your app's appearance will more closely resemble the look and feel of the Uber app, providing a familiar and user-friendly experience to your users.
In your home screen component, you can now add two additional UI components at the bottom of the screen, similar to the ones found in the Uber app. These buttons will enable users to choose a ride and proceed with the booking process. Although they won't have any actual functionality at this stage, you can certainly enhance them further to include more functionality as needed. By adding these buttons, your app's appearance will more closely resemble the look and feel of the Uber app, providing a familiar and user-friendly experience for your users.
To create a "NavFavorites" component that resembles the "Go Home" or "Go Here" options typically found in navigation apps, simply define a new functional component in your app. This component will display the favorite destinations, allowing users to easily access their preferred locations.
To create the NavFavorites
component, start by making a new file called navFavorites.js
in your components folder. Then, create a functional component and export it with a StyleSheet:
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const NavFavorites = () => {
return (
<View>
<Text>This is where the favorites live.</Text>
</View>
);
};
const styles = StyleSheet.create({});
export default NavFavorites;
This component will display the favorite destinations, allowing users to easily access their preferred locations. To implement this, you'll want to use a FlatList
component for displaying the favorite destinations in a scrollable list.
With the data prepared, you can now display it on the screen. The data can be placed outside the component to avoid clutter. The data will consist of three items: Home, Location, and Destination. These items can be coded to set the destination when clicked, if desired. The data structure will look like this:
const data = [
{
id: 1,
title: 'Home',
description: 'Home location'
},
{
id: 2,
title: 'Location',
description: 'Specific location'
},
{
id: 3,
title: 'Destination',
description: 'Destination of choice'
}
];
With this data in place, you can use the FlatList
component to display the favorite destinations in a scrollable list, allowing users to easily access their preferred locations.
Now, let's enhance the user interface by adding two buttons at the bottom of the screen, similar to the ones you'd find in the Uber app. These buttons will allow users to pick a ride and proceed with the booking process. To create these buttons, add UI components to your existing screen layout, making your app's appearance resemble the look and feel of the Uber app, providing a familiar and user-friendly experience.
To create a "NavFavorites" component that resembles the "Go Home" or "Go Here" options typically found in navigation apps, define a new functional component in your app. This component will display favorite destinations, allowing users to easily access their preferred locations. Start by creating a new file called navFavorites.js
in your components folder, then create a functional component and export it with a StyleSheet:
import React from 'react';
import { StyleSheet } from 'react-native';
const NavFavorites = () => { ... };
const styles = StyleSheet.create({ ... });
export default NavFavorites;
To implement this component, use a FlatList
component for displaying the favorite destinations in a scrollable list. Prepare the data consisting of three items: Home, Location, and Destination, and place it outside the component:
const data = [
{ ... },
{ ... },
{ ... },
];
With this data in place, you can use the FlatList
component to display the favorite destinations in a scrollable list, allowing users to easily access their preferred locations. Feel free to experiment with different origin and destination points to ensure the map view continues to adapt and provide a smooth experience for your users.
Now that you've made significant progress, take a moment to appreciate the quality of your work. Your dedication is evident, and the community's response of 1.7k likes is a testament to your hard work. With this momentum, you can continue to create amazing content for React Native. The future looks promising, and there's no doubt that you'll keep delivering exceptional value. Stay motivated and keep pushing the boundaries of what's possible in React Native development.
Now, let's implement the FlatList
component within the NavFavorites
component. First, import the FlatList
at the top of your navFavorites.js
file. Then, add the FlatList
as a self-closed component within the NavFavorites
component. It will require three properties: data
, keyExtractor
, and renderItem
.
For the data
property, pass in the data
array created earlier. For the keyExtractor
, pass in an arrow function that takes the item
as an argument and returns the item.id
. This will ensure that each item has a unique key during the loop. Finally, for the renderItem
, pass in an arrow function that takes the item
as an argument and returns a TouchableOpacity
component for each item.
import React from 'react';
import { StyleSheet, FlatList, TouchableOpacity } from 'react-native';
const NavFavorites = () => (
<FlatList
data={data}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<TouchableOpacity>
...
</TouchableOpacity>
)}
/>
);
const styles = StyleSheet.create();
export default NavFavorites;
The TouchableOpacity
component will render as a touchable button for each favorite destination, allowing users to easily access their preferred locations. Don't forget to experiment with different origin and destination points to ensure the map view continues to adapt and provide a smooth experience for your users.
To create touchable buttons for the favorite destinations, add a TouchableOpacity
component inside the FlatList
's renderItem
function. For now, simply add a text component with the text "test" as a child of the TouchableOpacity
:
import React from 'react';
import { ... } from 'react-native';
const NavFavorites = () => (
<FlatList
data={data}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<TouchableOpacity>
<Text>Test</Text>
</TouchableOpacity>
)}
/>
);
const styles = StyleSheet.create();
export default NavFavorites;
Now, import the NavFavorites
component into your home screen component. After doing so, you should see the "test" text displayed twice, corresponding to the two touchable buttons:
// In your home screen component
import NavFavorites from './components/navFavorites';
You can use these touchable buttons in multiple locations within your app, such as on the home screen and another screen. Additionally, consider adding a menu button that navigates back to the main menu, allowing users to return without swiping back.
Now that you have created the NavFavorites
component and implemented the FlatList
with touchable buttons, it's time to appreciate the progress you have made. You are moving at a great pace and delivering exceptional value with this React Native project. The future looks promising as you continue to push the boundaries of what's possible in React Native development. Keep up the incredible work and remember that you are truly making a difference in the development community.
Continuing with the NavFavorites
component, you'll want to add some icons within the TouchableOpacity
component to enhance the visual appearance. This will make the favorite destinations more interactive and visually appealing for users.
To improve the visual appearance of the NavFavorites
component, you can add icons to each of the items. For this purpose, you can use the icon set from React Native Elements. By rendering an icon with specific styles, you can create a more interactive and visually appealing interface for users to access their favorite destinations. In the next steps, you'll learn how to apply these styles and incorporate icons into your component.
In order to display the icons for each favorite destination, you'll need to render the icons within the TouchableOpacity
component. Utilize Tailwind CSS to style the icons with marginRight: 4
, borderRadius: 4
, backgroundColor: 'gray-300'
, and padding: 3
. To access the icon property from the data
array, you can use ES6 destructuring within the FlatList
's renderItem
function. This allows you to directly access the location
, destination
, and icon
properties from each item in the data
array. Now, when you run your app, you should see the icons displayed for each favorite destination, enhancing the visual appearance and interactivity of the NavFavorites
component.
With the NavFavorites
component created and the FlatList
implemented, you've made significant progress in enhancing the user interface of your app. Although the functionality is in place, it's important to keep the visual appearance and interactivity in mind. One way to improve this aspect is by incorporating icons into each favorite destination item.
To add icons to each item, you can use the icon set from React Native Elements. By applying specific styles to these icons, you'll create a more visually appealing and interactive experience for users accessing their favorite destinations. In order to display the icons within the TouchableOpacity
component, you'll need to style them using Tailwind CSS, with properties like marginRight: 4
, borderRadius: 4
, backgroundColor: 'gray-300'
, and padding: 3
.
Additionally, you can use ES6 destructuring within the FlatList
's renderItem
function to access the location
, destination
, and icon
properties from each item in the data
array. With these modifications, your app will display icons for each favorite destination, enhancing both the visual appearance and interactivity of the NavFavorites
component.
As you continue to develop your React Native project, remember to stay motivated and inspired by the positive feedback from the community. Keep pushing the boundaries of what's possible in React Native development, and you'll continue to deliver exceptional value.
You've made great progress so far, and now it's time to clean up the appearance of your favorite destinations. Currently, the items in the NavFavorites
component are displayed as two large blocks, which isn't very visually appealing. Recall that, by default, items are displayed in columns.
To improve the appearance, you'll need to adjust the styling of the TouchableOpacity
component and its children. Keep experimenting with different styles and layouts until you achieve a more visually pleasing result. As you continue to develop your React Native project, never forget the importance of a clean and user-friendly design, as this will greatly impact the overall user experience.
To refine the appearance of the favorite destinations in the NavFavorites
component, you can adjust the styling of the TouchableOpacity
component and its children. Start by setting the style of the TouchableOpacity
component to tailwind('flex-row items-center p-5')
. This makes the items display in a row, centers them, and applies padding.
Next, create a View
component with two Text
components inside it. Set the first Text
component to display the location
, and the second one to display the destination
. With these changes, your favorite destinations should now appear more organized and visually appealing, providing users with a better overall experience.
Here's how your updated TouchableOpacity
component should look:
<TouchableOpacity style={tailwind('flex-row items-center p-5')}>
<View>
<Text>{location}</Text>
<Text>{destination}</Text>
</View>
</TouchableOpacity>
As you progress with your React Native project, always remember the importance of clean and user-friendly design, as it significantly impacts the user experience.
To finalize the styling for the favorite destinations, you can apply additional styles to the location
and destination
text components. For the location
, set the style to tailwind('font-semibold text-lg')
, which will make the text larger and semi-bold. For the destination
, set the style to tailwind('text-gray-500')
, which will give the text a gray color. With these styling adjustments, your favorite destinations should now look polished and visually appealing.
Here's how your updated Text
components should look:
<View>
<Text style={tailwind('font-semibold text-lg')}>{location}</Text>
<Text style={tailwind('text-gray-500')}>{destination}</Text>
</View>
Remember, a well-designed user interface is crucial for providing a positive user experience in your React Native project.
Tailwind CSS offers a versatile color palette that can significantly enhance your app's appearance. After styling your favorite destinations, you might want to add separators between items in the FlatList
. To do this, you can use the ItemSeparatorComponent
prop, which accepts an arrow function that returns JSX.
In this case, you'll create a self-closing View
component with a style that uses Tailwind CSS classes. For example, you can set the background color using bg-gray-200
. However, when it comes to the height, Tailwind CSS might not provide the granularity you need. If you set the height to 1 using Tailwind CSS, the separator line may appear too thick, and you cannot go lower than that.
Here's an example of how to add an ItemSeparatorComponent
with a custom style:
<ItemSeparatorComponent={() => (
<View style={...}></View>
)} />
Keep in mind that you may need to adjust the separator's style to achieve the desired appearance for your app.
To create a thinner separator line between the items in the FlatList
, you can combine Tailwind CSS styles with inline styles. Use an array syntax for the style prop and set the height to 0.5. This will give you a super thin line that serves as a divider between the components. Here's an example of how to mix Tailwind CSS and inline styles in the ItemSeparatorComponent
:
<ItemSeparatorComponent={() => (
<View style={[tailwind('bg-gray-200'), {height: 0.5}]} />
)} />
With this thin separator line, your favorite destinations list will look even more polished and visually appealing. As you continue developing your app, ensure that elements like these enhance the user experience and maintain a clean and user-friendly design.
In the MapScreen component, locate the NavigateCard component. To include the NavFavorites
component, simply add it right below the View
component within the NavigateCard. This will ensure that your favorite destinations are displayed within the navigation card.
Here's an example of how you should add the NavFavorites
component inside the NavigateCard:
<NavigateCard>
<View>
...
</View>
<NavFavorites />
</NavigateCard>
With this addition, you should see the favorite destinations appear in the navigation card, providing a more complete and visually appealing interface for users to access their favorite destinations.
Incorporating reusable components is one of the key strengths of React, as it allows you to significantly improve your app's appearance and functionality. By utilizing these components, you can create a more cohesive and visually pleasing interface while maintaining consistent design patterns throughout your application. In this case, you've successfully integrated the NavFavorites
component, upgrading the overall look and feel of your app. Remember that reusable components save time and effort, and they contribute to a more maintainable codebase. Keep embracing the power of React components to continue delivering exceptional value to users.
As a challenge, try expanding the functionality of your app by allowing users to quickly navigate to their favorite destinations with a single tap. Since you've already learned how to handle onPress events and set destinations, you should be able to implement this feature. By adding this shortcut, you'll enhance the user experience, making your app even more convenient and efficient. You've come a long way in building your app, and with your new skills, you can continue to create outstanding features. Keep up the great work!
At this stage, you may have noticed an issue with the layout when the keyboard is activated. When you open the keyboard, it overlaps the bottom part of the screen, making it difficult to interact with the interface, especially on smaller devices. This is not a good user experience. To address this problem, you can use the KeyboardAvoidingView
component provided by React Native.
KeyboardAvoidingView
is a wrapper component that automatically adjusts the layout when the keyboard is opened, ensuring that the content remains visible and accessible. By wrapping your existing components with KeyboardAvoidingView
, you can create a more user-friendly interface that adapts to the presence of the on-screen keyboard.
To address the issue with the layout overlapping the keyboard, you will need to use the KeyboardAvoidingView
component from React Native. This component will automatically adjust the layout when the keyboard is opened, ensuring that the content doesn't get covered. First, import the KeyboardAvoidingView
from React Native:
import { KeyboardAvoidingView } from 'react-native';
Then, wrap your existing components with the KeyboardAvoidingView
to create a more user-friendly interface that adapts to the presence of the on-screen keyboard.
To fix the overlapping keyboard issue, you can utilize the KeyboardAvoidingView
component. First, remove the unnecessary StyleSheet
imports, since you won't be needing them. Next, import the KeyboardAvoidingView
from React Native:
import { KeyboardAvoidingView } from 'react-native';
Position the KeyboardAvoidingView
component above the Navigator
level, but below the SafeAreaProvider
for the icons. Then, wrap all the Navigator
contents inside the KeyboardAvoidingView
component.
To make the content of the KeyboardAvoidingView
visible, you need to give it a flex of 1. There are two methods to achieve this: using Tailwind CSS or inline styling. In this example, we're going to use inline styling:
<KeyboardAvoidingView style={{ flex: 1 }}>
{/* Navigator contents */}
</KeyboardAvoidingView>
With the KeyboardAvoidingView
component in place and styled, your app should now adapt to the presence of the on-screen keyboard, providing a better user experience.
To ensure the KeyboardAvoidingView
component works correctly on both iOS and Android platforms, you need to add a behavior attribute. Since the behavior differs between platforms, you can use the Platform
provided by React Native to handle the differences. First, import Platform
from React Native:
import { Platform } from 'react-native';
Then, set the behavior
attribute of the KeyboardAvoidingView
component based on the platform. For iOS, use padding, while for Android, use height:
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
</KeyboardAvoidingView>
With this configuration, the KeyboardAvoidingView
component will adjust the layout properly on both iOS and Android devices when the keyboard is opened, improving the user experience across different platforms.
Now that the layout moves up when the keyboard is activated, you can comfortably type and see all the search results. To further enhance the experience, you can add a keyboardVerticalOffset
attribute to the KeyboardAvoidingView
component. The value for this attribute is platform-specific. For iOS, use -64
to crop the bottom component by 64 units, while for Android, use 0
since it's handled automatically. This adjustment ensures the keyboard doesn't overlap with any bottom UI elements:
<KeyboardAvoidingView
style=}
behavior=}
keyboardVerticalOffset=>
</KeyboardAvoidingView>
With these changes, your app's user experience becomes even better as it now handles the keyboard interaction seamlessly on both iOS and Android devices. Keep refining your app's design and functionality to deliver outstanding value to your users.
Continuing with the development, we are about to implement the distance matrix for the Google API. This feature will enhance the functionality of your app, allowing users to access essential information like distance and travel time between destinations. As you keep adding new features, your app becomes more powerful and valuable to users, so keep up the great work and continue refining your app's design and functionality.
Maximize Your Learning Experience: Enhance Your Skills with React Basics 101 and Upgrade Your App's Functionality
It's fantastic to hear that you've registered online and are excited for the lessons being provided by instructors like Pablo and Ibrahim. As you progress through the course, you'll complete the navigator section and move on to more advanced features like time traveling. Don't forget to take advantage of additional resources like Skillshare's React Basics 101 course. Even if you're already familiar with React, it's always beneficial to revisit the fundamentals and solidify your understanding. Many learners recommend binge-watching these valuable resources to quickly enhance their skills. Keep up the enthusiasm and dedication as you continue to learn and grow in your development journey!
To further improve your React skills, take advantage of the free month of Skillshare that comes with the React Basics 101 course. This resource will help solidify your understanding of React fundamentals and prove beneficial in your development journey. Many learners find it helpful to binge-watch these valuable resources to quickly enhance their skills. Make sure to seize this opportunity, as it will undoubtedly aid your growth and learning experience.
To further enhance your app, head back to the map screen and navigate to the NavigateCard
. Inside the NavigateCard
, add a new View
component beneath the existing content. This new View
will include a button for selecting different rides options.
<View>
{/* Add your rides button here */}
</View>
By incorporating this feature, you provide users with more flexibility in choosing their preferred ride options and improve the overall user experience.
With the current layout, you have a rides button, and you want to add another button for Uber Eats. To achieve this, create a View
component underneath the existing content inside the NavigateCard
. Inside this new View
, add two TouchableOpacity
components – one for rides and one for Uber Eats. This setup mimics the design and functionality of the standard Uber app, providing users with more flexibility in choosing their preferred options.
<View>
<TouchableOpacity>
{/* Rides Button */}
</TouchableOpacity>
<TouchableOpacity>
{/* Uber Eats Button */}
</TouchableOpacity>
</View>
By incorporating these buttons, you improve the overall user experience, as users can easily switch between ride and food delivery options. And, if you enjoy the React Basics content on Skillshare, please consider leaving a positive review to help other students discover the course, which in turn supports the growth of the platform and its instructors.
As Arthur suggests, it's essential to keep grinding and expanding your knowledge in various areas of web development. Focusing on CSS and React concepts, such as Redux and Mojito, can significantly enhance your skillset. In fact, this particular build incorporates a lot of Redux, which might make it seem intimidating. However, delving into these advanced concepts will help you become a more well-rounded developer and tackle more complex projects with ease. So, continue to challenge yourself and explore different aspects of web development to boost your expertise and proficiency.
To build upon the NavigateCard
, add a new View
component inside it. Within this View
, create a TouchableOpacity
component that includes an Icon
and some text. The Icon
will come from Font Awesome and represent a car with a white color and size 16. Use Tailwind styling for the text, making it white and centered, with the content saying "rides". Don't forget to add the necessary imports for the Icon
and TouchableOpacity
components. Your code should now resemble this:
<View>
<TouchableOpacity>
<Icon name="car" color="white" size={16} />
<Text style={styles.text}>rides</Text>
</TouchableOpacity>
</View>
With this addition, users can now interact with the "rides" button, further improving the app's functionality and user experience.
Now, let's modify the TouchableOpacity
component to include a background color and improve the button's appearance. Start by adding a style
attribute to the TouchableOpacity
component, with the following styling options: flex
, flex-row
, bg-black
, w-24
, px-4
, py-3
, and rounded-4
. These styles will make the button look better and more prominent. Here's the updated code for your TouchableOpacity
component:
<TouchableOpacity style={Tailwind('flex flex-row bg-black w-24 px-4 py-3 rounded-4')}>
<Icon name="car" color="white" size= />
<Text style=>rides</Text>
</TouchableOpacity>
Thanks to Tailwind, you can quickly create a sleek and well-designed button with minimal effort. The updated "rides" button is now visible and has a more polished appearance, enhancing the user experience in your app.
With the "rides" button in place, you can quickly create a similar button for Uber Eats by copying the TouchableOpacity
component and making a few adjustments. Replace the bg-black
styling with a transparent background and change both the icon and text colors to black instead of white. This will give you a visually appealing button that contrasts nicely with the "rides" button. The updated code should look like this:
<TouchableOpacity>
<Icon name="car" color="black" size= />
<Text style=>Uber Eats</Text>
</TouchableOpacity>
By default, the View
component stacks its children in a column, which is why the buttons are displayed one above the other. With these changes, users can easily switch between ride and food delivery options, improving the overall user experience.
To style the View
component, set its style attribute to use Tailwind with the following styles: flex-row
, bg-white
, justify-evenly
, py-2
, mt-auto
, border-t
, and border-gray-100
. This will arrange the child components in a row, create even spacing between them, add padding on the vertical axis, push the content to the bottom, and apply a border at the top. Your updated code should look like this:
<View style={Tailwind`flex-row bg-white justify-evenly py-2 mt-auto border-t border-gray-100`}>
...
</View>
With these styling changes, your app's layout will be more polished and visually appealing. It's essential to have a great coding playlist to keep you motivated and focused while developing your app, so keep enjoying your coding sessions and continue enhancing your app's design and functionality!
Now, with the added border at the top, the layout looks even better. However, the "rides" button appears a bit squashed. To fix this issue, you might need to adjust the styles applied to the TouchableOpacity
component. Keep refining your app's design and functionality to achieve the best possible user experience. Remember, having a great coding playlist can help you stay motivated and focused during development sessions. So, keep enjoying your coding sessions and continue enhancing your app!
To fix the squashed appearance of the "rides" button, you will need to add the justify-between
style to the TouchableOpacity
component containing the icon. This style creates space between the elements inside the component, improving the layout. Update the TouchableOpacity
component with the justify-between
style as shown:
<TouchableOpacity style=>
<Icon name="car" color="white" size= />
<Text style=>rides</Text>
</TouchableOpacity>
Now, the "rides" button looks better and well-spaced, enhancing the overall layout and user experience. Make sure to keep refining your app's design and functionality to achieve the best possible user experience.
To enable navigation to the next screen when the "rides" button is pressed, add an onPress
attribute to the TouchableOpacity
component. This attribute will trigger the navigation.navigate
function, which requires the navigation
prop to be imported. Set the onPress
attribute to navigate to the RideOptionsCard
screen. Your updated TouchableOpacity
component should look like this:
<TouchableOpacity onPress={() => navigation.navigate('RideOptionsCard')}>
<Icon name="car" color="white" size= />
<Text style=>rides</Text>
</TouchableOpacity>
Now, when users click on the "rides" button, they will be taken to the next screen, further improving the app's functionality and user experience.
With the current layout, the app now displays a "Select a Ride" header at the top of the screen, followed by a flat list of car options. The data for these options includes unique IDs, titles, and cost multipliers. For example, UberX has a multiplier of 1, UberXL has a multiplier of 1.2, and UberLux has a multiplier of 1.75. Each option also has a corresponding image.
To add the back button functionality, wrap the TouchableOpacity
component containing the Chevron left icon from Font Awesome in a View
component. Then, use absolute positioning with Tailwind to place the button at the desired location. When pressed, the back button should navigate to the previous screen. To achieve this, use the navigation.navigate()
function, passing the name of the previous screen, which in this case is the NavigateCard
.
The next step is to render a list of car options using the FlatList
component. Make sure to import it and set the data
attribute to the car options data created earlier. This will display the available ride options, such as UberX, UberXL, and UberLux, along with their respective cost multipliers and images.
To display the car options in a list, use the FlatList
component. First, pass the car options data to the data
attribute of the FlatList
. Then, set the keyExtractor
attribute to extract the keys from the items' IDs. To render each item in the list, use the renderItem
attribute, which takes an arrow function that deconstructs the item
from the list.
Inside the arrow function, use a TouchableOpacity
component to display each car option. Initially, add a Text
component inside the TouchableOpacity
with the text "car" to see if three cars are rendered on the screen.
<FlatList
data={...}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<TouchableOpacity>
<Text>car</Text>
</TouchableOpacity>
)}
/>
Now that you can see the three car options, replace the Text
component with an Image
component to display the actual car images. Set the image's style
attribute to have a width and height of 100 and set the resizeMode
to "contain" to maintain the aspect ratio. Finally, set the source
attribute to the image of the current item in the list.
<Image
style={{
width: 100,
height: 100,
resizeMode: 'contain',
}}
source={{ uri: item.image }}
/>
With these changes, the car options are now displayed as images in the list, making it easier for users to choose the desired ride option. Keep refining your app's design and functionality to provide the best possible user experience.
To destructure the item and display the title, multiplier, and image, you can use the following code inside the renderItem
arrow function of the FlatList
component:
const { id, title, multiplier, image, ...item } = item;
With this destructuring, you can still access the full item
object. Next, replace the "car" text inside the TouchableOpacity
with an Image
component displaying the car images. Set the image's style
attribute to have a width and height of 100 and set the resizeMode
to "contain" to maintain the aspect ratio. Set the source
attribute to the image of the current item in the list:
<Image
style={{ width: 100, height: 100, resizeMode: 'contain' }}
source={{ uri: image }}
/>
Then, create a View
component containing two Text
components to display the title and an estimated travel time, such as "UberX, travel time...". Style the TouchableOpacity
with Tailwind to arrange the elements in a row, center them, and justify the items between themselves. Add padding, font size, and other styles to refine the appearance.
To keep track of the selected car option, create a piece of state called selected
and use the setSelected
function to update it when a car option is clicked:
const [selected, setSelected] = useState(null);
In the onPress
attribute of the TouchableOpacity
, set the selected item:
<TouchableOpacity onPress={() => setSelected(item)}>
Now, conditionally apply a background color to the selected car option by comparing the ID of the current item with the selected item's ID:
style={tailwind(`... ${id === selected?.id ? 'bg-gray-200' : ''}`)}
With these changes, the car options are displayed as images in a list, and users can select their desired ride option. Keep refining your app's design and functionality to provide the best possible user experience.
Now, when selecting a car option, you'll notice that the background color changes, indicating the selected car. This forms the basis of the button functionality. Combining Tailwind, React Native, and the previous changes, you can further refine the appearance of the button. To center the text, set the text color to white, and increase the font size, use the following Tailwind classes:
style={`text-center text-white text-xl`}
This will ensure that the text is centered, white, and has an extra-large font size. Keep refining your app's design and functionality to provide the best possible user experience.
To improve the appearance of the button, you can set its background color to black and add padding and margin to create some space around it. Use the Tailwind utility classes for this purpose. For example, you can set the background color to black with bg-black
, add padding on the Y-axis with py-3
, and set a margin of 3 with m-3
. By doing so, the button looks better and more visually appealing. This ensures that the button is not touching the edges of the screen, and because it's within the safe area, it won't be obstructed by any device-specific elements.
style={Tailwind('bg-black py-3 m-3')}
Feel free to continue refining your app's design and functionality to provide the best possible user experience for your users.
To further enhance your app, don't forget to register for the upcoming React to Airbnb challenge happening next Tuesday. This event will span five days and involve coding React in Airbnb. With £10,000 worth of prizes up for grabs, it's an excellent opportunity for both newcomers and experienced developers to learn and have fun. Make sure to sign up using the registration link provided and join this exciting event.
To make the button disabled when no car option is selected, add a disabled
attribute to the TouchableOpacity
component. Set its value to true
when there is no selected item:
<TouchableOpacity disabled=>
Additionally, conditionally apply a grey background to the button when there is no selected item by using the bg-grey-300
Tailwind class:
style=`)}
Now, when navigating back and forth between screens, the button will appear disabled and have a grey background until a car option is selected. Once an option is chosen, the button becomes enabled and changes its background color. This enhances the app's user experience by clearly indicating when the button is active and usable. Keep refining your app's design and functionality to provide the best possible user experience.
To complete the build, we'll integrate the Google Distance Matrix API, which allows us to calculate the total distance from point A to point B. This enables users to select a ride with the distance in miles, get an estimated time for reaching the destination, and use the relevant information from the distance matrix to calculate a price that can be charged to the customer. Integrating this API will enhance the app's functionality and provide more valuable information to users, making their experience even better.
With the current implementation, the next step is to manage the logic for the Google Distance Matrix API integration. As the necessary details are stored around the map, it makes sense to handle this logic inside the Map
component. This integration will allow the app to calculate the total distance between two points, provide an estimated travel time, and offer a calculated price based on the selected ride option. By incorporating the Google Distance Matrix API, you enhance the app's functionality and deliver a more valuable experience to users.
Create a useEffect
that is responsible for calculating the travel time. This useEffect
will depend on the origin, destination, and the Google Maps API key. By making the useEffect
dependent on these variables, you ensure that the travel time is accurately calculated whenever any of these values change.
useEffect(() => {
// Calculate the travel time here
}, [origin, destination, googleMapsApiKey]);
With the app layout displaying a "Select a Ride" header and a list of car options, it's time to enhance its functionality by integrating the Google Distance Matrix API. This API allows for calculating the travel time between the origin and destination. Since the request to the API is asynchronous, you'll need to handle it accordingly.
To manage this integration, create a useEffect
that is responsible for calculating the travel time. This useEffect
should depend on the origin, destination, and the Google Maps API key. By making the useEffect
dependent on these variables, you ensure that the travel time is accurately calculated whenever any of these values change.
useEffect(() => {
// asynchronous code for the Google Distance Matrix API request
}, [origin, destination, googleMapsApiKey]);
By incorporating the Google Distance Matrix API, you not only enhance the app's functionality but also provide a more valuable experience to users. Continue refining your app's design and functionality to deliver the best possible user experience.
Now, you should push the calculated travel time into Redux. This way, you can access it in the other component – the one where users select a ride – by using the travel time selector. This approach allows you to seamlessly share data between components, ensuring that relevant information is available when needed, resulting in a better user experience.
In order to calculate the travel time within the useEffect
, you'll need to create an asynchronous function, getTravelTime
, inside the useEffect
and then invoke it. You can't use async
directly inside useEffect
since it breaks the rules of hooks. To create an asynchronous block of code, define the getTravelTime
function inside the useEffect
and then call it like so:
useEffect(() => {
const getTravelTime = async () => {
// Your asynchronous code here
};
getTravelTime();
}, [origin, destination, googleMapsApiKey]);
By following this approach, you can execute asynchronous code within your useEffect
without violating any rules of hooks. This will allow you to integrate the Google Distance Matrix API and calculate the travel time, ultimately enhancing the app's functionality and user experience. Keep refining your app's design and functionality to provide the best possible user experience.
Continuing with the integration of the Google Distance Matrix API, it's crucial to add a safeguard to ensure that the API is only called when both origin and destination are defined. To do this, you can include a condition at the beginning of the useEffect
:
if (!origin || !destination) return;
This check ensures that the API call is only made when the origin and destination are available. You can place this line inside the getTravelTime
function or at the beginning of the useEffect
.
The API call itself is made using a fetch
request, which is essentially a GET request to the Google API. By incorporating this safeguard and making the API call, you further enhance the app's functionality and user experience. Keep refining your app's design and functionality to provide the best possible user experience.
In order to make a request to the Google Distance Matrix API, you'll need to construct a query URL. The base URL for the API call is:
https://maps.googleapis.com/maps/api/distancematrix/json
You can use template literals (backticks) to interpolate JavaScript values into the URL. Set the units
parameter to imperial
and pass the origin
, destination
, and key
(Google Maps API key) as parameters:
const url = `https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=${origin.description}&destinations=${destination.description}&key=${googleMapsApiKey}`;
Here, the origin
and destination
values are obtained from Redux. By making a fetch
request to this URL, you can retrieve the travel time information, further enhancing the app's functionality and user experience. Continue refining your app's design and functionality to provide the best possible user experience.
To fetch the travel time information from the Google Distance Matrix API, you can use the fetch
function to make a request to the constructed query URL. Once the request is made and a response is received, you'll need to parse the response as a JSON object. Here's how you can do that:
fetch(url)
.then(response => response.json())
.then(/* handle the JSON object */);
By fetching and handling the travel time information from the API, you further enhance your app's functionality and user experience. Continue refining your app's design and functionality to provide the best possible user experience.
After fetching the travel time information from the Google Distance Matrix API, you can access the data using .then()
. This data contains valuable information, such as the time it takes to travel from point A to point B.
fetch(url)
.then(response => response.json())
.then(data => {
// handle the data object
});
By handling the data returned from the API, you're able to further enhance your app's functionality and provide a better user experience. Continue refining your app's design and functionality to deliver the best possible experience for your users.
Now that the travel time information has been fetched from the Google Distance Matrix API, it's time to dispatch the data to Redux. To demonstrate that the data is being fetched correctly, you can log the data to the console:
fetch(url)
.then(response => response.json())
.then(data => {
console.log(data);
});
Upon logging the data, you can see the destination, origin, distance, and travel time. There's also a valuable value available that can be used to calculate the price. With this information in hand, you've successfully integrated the Google Distance Matrix API into your app. This not only enhances the app's functionality but also provides a more valuable experience to users. Continue refining your app's design and functionality to deliver the best possible user experience.
To store the travel time information in Redux, you'll need to use a dispatch. First, get the dispatch by declaring a variable dispatch
and assigning it to useDispatch()
. Then, call the dispatch function with the action setTravelTimeInformation
:
const dispatch = useDispatch();
// ...
fetch(url)
.then(response => response.json())
.then(data => {
dispatch(setTravelTimeInformation(data));
});
By storing the travel time information in Redux, you enable seamless sharing of data between components, ensuring relevant information is available when needed and resulting in an improved user experience. Keep refining your app's design and functionality to provide the best possible experience for your users.
Now that you've fetched the travel time information from the Google Distance Matrix API, you only want to extract the relevant data to store in Redux. To save you some time, you can access the required information directly by navigating through the returned object like this:
const relevantData = data.rows[0].elements[0];
This line of code extracts the necessary information from the API response, which includes the travel time, distance, and other valuable data. With this information extracted, you can now dispatch it to Redux and continue refining your app's design and functionality to provide an improved user experience.
With the travel time information obtained and dispatched to the Redux global store, you can now access this data from any component in your application. This ensures seamless sharing of information between components, enhancing the overall user experience. Continue making improvements to your app's design and functionality to provide the best possible experience for your users.
In the RideOptions
component, you can access the travel time information stored in Redux. To do this, create a variable called travelTimeInformation
and use the useSelector
hook from React Redux to select the travel time information. Make sure to import useSelector
and selectTravelTimeInformation
from their respective modules. Here's how you can do it:
import { useSelector } from 'react-redux';
import { selectTravelTimeInformation } from '../path/to/navSlice';
// Inside the RideOptions component
const travelTimeInformation = useSelector(selectTravelTimeInformation);
By utilizing the useSelector
hook and selecting the travel time information from Redux, you can now access this data within the RideOptions
component. This enables seamless sharing of information between components, further enhancing the user experience. Keep improving your app's design and functionality to provide the best possible experience for your users.
Inside the RideOptions
component, you now have access to the travel time information, which allows you to display the selected ride and its corresponding travel time. To do this, inside the component, you can use the following code:
const displayText = `Selected Ride - Travel Time: ${travelTimeInformation?.distance?.text}`;
This code creates a string called displayText
which includes the travel time and distance information from the travelTimeInformation
object. By utilizing this information, you can enhance the user experience by providing them with the relevant details about their chosen ride. Keep improving your app's design and functionality to provide the best possible experience for your users.
Inside the RideOptions
component, you can now display the distance information for the selected ride by accessing the distance.text
property of the travelTimeInformation
object. For example, if you want to display the distance between London and Essex, the output will show "32.6 miles" as the distance. By incorporating this information into your app, you can provide users with more accurate and helpful details about their chosen rides, further enhancing the overall user experience. Keep refining your app's design and functionality to offer the best possible experience for your users.
Now, let's display both the travel time and distance in the displayText
string. Instead of just showing the travel time, you can also include the distance by accessing the distance.text
property from the travelTimeInformation
object. Here's how you can update the displayText
string:
const displayText = `Selected Ride - Travel Time: ${travelTimeInformation.duration.text} - Distance: ${travelTimeInformation.distance.text}`;
This will display both the travel time and distance for the selected ride. For instance, when selecting a ride from London to Germany, it will show "10 hours, 24 minutes" as the travel time and "53 miles" as the distance. With this additional information, your app becomes even more informative and useful for your users. As always, continue refining your app's design and functionality to provide the best possible experience for your users.
Now you're ready to calculate the price for the ride based on the distance and travel time. Stay tuned for the final piece of the puzzle!
To calculate the price for the ride based on distance and travel time, you can use a custom formula. First, create a constant representing a surge charge rate, which can be modified based on demand:
const surgeChargeRate = 1.5;
Next, use the Internationalization JavaScript API to format the price. This built-in API allows you to format a piece of text using your custom formula. The formula calculates the price based on the travel time duration value, the surge charge rate, and each car's multiplier:
const price = new Intl.NumberFormat('en-GB', {
style: 'currency',
currency: 'GBP',
}).format(
(travelTimeInformation?.duration?.value * surgeChargeRate * carMultiplier) / 100
);
Now, you can display the price alongside the travel time and distance in the displayText
string:
const displayText = `Selected Ride - Travel Time: $ - Distance: $ - Price: ${price}`;
This will display the travel time, distance, and calculated price for the selected ride, providing users with a more informative and useful app. Keep refining your app's design and functionality to offer the best possible experience for your users. With all the features implemented, you now have a powerful Uber clone app built using React Native, Tailwind, and React Redux.
To make the icon pop out, set its background color to bg-gray-100
and give it a Z-index of 50, which determines the layering order on the screen. Add some padding with p-3
and round the corners using rounded-4
. To add a shadow behind the icon, use shadow-lg
. Now you should see a nice floating icon above everything else.
For navigation, first import the useNavigation
hook from React Navigation. Create a constant navigation
and assign it to the useNavigation()
function. When the icon is pressed, add an arrow function to navigate to the home screen by calling navigation.navigate("HomeScreen")
.
For example, let's say you want to navigate from Mumbai, India to another location. By implementing these changes, your app's navigation should now function smoothly, allowing users to select different locations with ease. Keep refining your app's design and functionality to provide the best possible experience for your users.
In this section, we encountered an issue when trying to calculate the travel time and distance from Mumbai to Dubai. The error was caused by an undefined value in the travelTimeInformation
object. To protect against this, you can use optional chaining while accessing the properties of the object. For example, update the displayText
string as follows:
const displayText = `Selected Ride - Travel Time: ${travelTimeInformation?.duration?.text} - Distance: ${travelTimeInformation?.distance?.text}`;
By doing this, we managed to resolve the error and successfully display the travel time and distance between different locations, including Mumbai to Dubai, and London to New Delhi. Remember, when encountering errors, it's crucial to keep calm, analyze the issue, and use your developer toolkit to find a solution.
In conclusion, we've built an impressive Uber clone app using React Native, Tailwind, and React Redux with features such as travel time, distance, and price calculation. Always strive to refine your app's design and functionality to provide the best possible experience for your users. Keep learning and improving your skills as a developer to tackle any challenges that come your way.
In this demo, we've showcased an Uber clone app that uses the Google Places API, Google Directions API, and Google Distance Matrix API. These APIs power features like autocomplete for location input, directions between two points, and calculating travel time and distance. The app has a sleek user interface with well-integrated UI elements.
For example, if you search for "London Bridge" and select "Get a Ride," the app will display the origin as London Bridge. You can then click on "Where to?" and input "Birmingham, UK" as your destination. The app will show the estimated cost of the ride as £132, with a distance of 127 miles. You can interact with the map, switch between origin and destination, and even select different ride options.
The app also includes optional styling, swiping functionality between screens, and a floating button to return to the home page. This demo highlights the power of combining various APIs with React Native, Tailwind, and React Redux to create a well-designed, feature-rich, and user-friendly app. Keep refining your app's design and functionality to provide the best possible experience for your users.
After completing the Uber clone app, you might be interested in deploying it. Deploying React Native apps is a different process altogether, so it's worth considering a separate video to cover that topic. In the meantime, share your progress on Instagram by tagging @SSSSanga and sharing your experience with the project.
If you enjoyed this tutorial and want to take part in more learning opportunities, there's an upcoming challenge at PapaReact.com with only a few days left before it begins. The five-day React challenge offers over £10,000 worth of prizes and covers a wide range of topics, including creating a portfolio, front-end build, calendar search functionality, Mapbox integration, and a React and Next.js build. As a bonus, you'll receive a 60-page book written by Sanga, valued at £50, for free when you sign up and participate in the challenge. Don't miss this opportunity to expand your knowledge and improve your skills as a developer!
In addition to the impressive Uber clone app, there are more learning opportunities available to help you further improve your skills as a developer. One such opportunity is the upcoming challenge at PapaReact.com, offering over £10,000 worth of prizes and covering various topics like creating a portfolio, front-end build, calendar search functionality, Mapbox integration, and a React and Next.js build. Make sure to reserve your spot, as seats are limited.
Furthermore, there is an exclusive Skillshare opportunity available, providing free access to the React Basics 101 class. This course covers all the fundamentals of React, and you can access it through the second link in the description for a free month. Don't miss these opportunities to expand your knowledge and become a better developer.
The Uber clone app built using React Native, Tailwind, and React Redux showcases the potential of combining various technologies and APIs to create a feature-rich, well-designed, and user-friendly application. If you're looking to further enhance your skills as a developer, consider participating in the upcoming challenge at PapaReact.com or check out the Zero to Full Stack Hero course.
These learning opportunities cover a wide range of topics, from creating a portfolio and front-end build to calendar search functionality, Mapbox integration, and React and Next.js build. Additionally, there's a free Skillshare course called React Basics 101, accessible through the second link in the description, which covers all the fundamentals of React. Don't miss out on these chances to expand your knowledge and become an even better developer.
In conclusion, the Uber clone app developed using React Native, Tailwind, and React Redux demonstrates the power of combining multiple technologies and APIs to create a feature-rich, well-designed application. With over 2,000 likes on the video and 30,000+ playbacks, it's evident that this was the biggest build ever done on the channel.
As a developer, never miss an opportunity to learn and grow. Make sure to register for the upcoming challenge at PapaReact.com and attend the Airbnb challenge next Tuesday. Keep pushing yourself to become better and continue expanding your knowledge. Stay tuned for more exciting content, and as always, see you in the next one!