Skip to content

UdayDey0909/Learning-React-Native

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

71 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Learning-React-Native

This Repo is sourced from the YouTube Channel Codevolution Playlist Doc

What is React Native?

React Native is an open-source framework for building native Android and iOS applications using React.

  • JavaScript to access platform-specific APIs (such as camera and push notifications)
  • React components to define the appearance and behavior of your user interface
  • React itself is a library designed for building user interfaces
  • react-dom for web apps and react-native for native mobile apps

Prerequisites

  • JavaScript
  • React fundamentals

Why React Native?

  • iOS apps require Swift or Objective-C.
  • Android apps require Java or Kotlin.
  • With approximately 31% on iOS and 68% on Android, you would typically need to develop two separate apps using different technologies.
  • With React Native, you can create an app that works seamlessly on both platforms.

Why React Native?

React Native is in high demand in the job market.

Rather than assembling separate teams of iOS and Android developers, businesses can opt for a single team proficient in React Native, saving time and costs.

If the team is well-versed in React, their expertise can extend to web app development as well.

Source Code Repo of this Playlist

https://github.com/gopinav/React-Native-Tutorials

Expo vs React Native

  • React Native is an open-source framework maintained by Meta (Facebook), designed for building cross-platform apps.
  • Expo is an open-source framework maintained by Expo itself, designed for building cross-platform apps.
  • Unlike plain RN, which can be challenging and time-consuming to set up, Expo provides a streamlined experience.
  • Expo has a suite of tools and services built around React Native, which greatly simplifies the development process.
  • With plain React Native, you cannot build iOS apps on a Windows or Linux machine. Native iOS apps can only be built on a macOS machine.
  • With Expo, you can develop your app on Windows or Linux and run your app on a physical iPhone.

Expo

  • Expo has evolved significantly over the years and now supports nearly all the features necessary for building mobile apps
  • In the event that you require access to native Android or iOS code, Expo allows you to eject your app and work with a plain React Native codebase

Environment Setup

https://reactnative.dev/docs/environment-setup

Core Components in React Native

React Native offers a collection of essential pre-built components known as Core Components, which are readily available for building your native app's user interface.

React Native UI Components Comparison

Below is a comparison of React Native's core UI components with their counterparts in Android, iOS, and Web:

React Native UI Component Android View iOS View Web Analog
<View> <ViewGroup> <UIView> A non-scrolling <div>
<Text> <TextView> <UITextView> <p>
<Image> <ImageView> <UIImageView> <img>
<ScrollView> <ScrollView> <UIScrollView> <div>
<TextInput> <EditText> <UITextField> <input type="text">

Explanation of Core Components

1. View

  • Acts as a container for other components.
  • Similar to <div> in web development.
  • Supports layout, styling, and touch handling.

2. Text

  • Used for displaying text.
  • Supports styling, nesting, and accessibility features.
  • Unlike HTML, <Text> components must be wrapped inside another <Text> for nesting.

3. Image

  • Displays images from a local or remote source.
  • Uses <Image> component instead of <img>.
  • Supports various formats and optimization options.

4. ScrollView

  • Used to create scrollable views.
  • Renders all child components at once (unlike FlatList which supports lazy loading).
  • Supports both horizontal and vertical scrolling.

5. TextInput

  • Allows user input, like an input field in HTML.
  • Supports features like keyboard type, auto-correction, and secure text entry (for passwords).

Why Use React Native Core Components?

  • Cross-platform compatibility: Write once, run on both Android and iOS.
  • Performance: Uses native views for efficient rendering.
  • Ease of development: Provides built-in UI components similar to web and native elements.

React Native’s core components simplify the development process by offering pre-built, optimized UI elements that function seamlessly across platforms.

View

The View component is a fundamental core component in React Native that serves as a building block for creating user interfaces.

It functions as a container that supports layout using Flexbox, styling, touch handling, and accessibility controls.

In web development terms, the View component can be compared to the <div> tag.

The View component is typically nested inside other views and can have zero or more children of any type.

Example Usage

Import:

import { View, Text } from "react-native";

Basic View

<View
   style={{
      width: 200,
      height: 100,
      backgroundColor: "#peach",
   }}
>
   <Text style={{ color: "#fff", fontSize: 18 }}>Hello, View!</Text>
</View>

Nested Views

<View
   style={{
      padding: 20,
      backgroundColor: "#f5f5f5",
   }}
>
   <View
      style={{
         width: 100,
         height: 100,
         backgroundColor: "#e74c3c",
      }}
   >
      <Text style={{ color: "#fff" }}>Box 1</Text>
   </View>

   <View
      style={{
         width: 100,
         height: 100,
         backgroundColor: "#2ecc71",
      }}
   >
      <Text style={{ color: "#fff" }}>Box 2</Text>
   </View>
</View>

Text

The Text component is used for displaying text.

  • It supports nesting, styling, and touch handling.

In web development terms, the Text component can be compared to the <p> tag.

Depending on the target platform, React Native will translate this component to:

  • UITextView (iOS)
  • TextView (Android)
  • <p> (Web)

Example Usage

Import

import { Text, View } from "react-native";

Basic Text

<Text>Hello, React Native!</Text>

Styled Text

<Text style={{ fontSize: 20, fontWeight: "bold", color: "#2c3e50" }}>
   Styled Text
</Text>

Nested Text

<Text style={{ fontSize: 18 }}>
   This is <Text style={{ fontWeight: "bold" }}> nested </Text> text.
</Text>

Colored Text

<Text style={{ color: "red" }}>Red Text</Text>
<Text style={{ color: "green" }}>Green Text</Text>
<Text style={{ color: "blue" }}>Blue Text</Text>

Image

The Image component enables us to display various types of images, including:

  • Static images
  • Network images
  • Images from the local disk, such as the camera roll

Image supports styling, nesting, and accessibility features.

React Native seamlessly translates the Image component to platform-specific counterparts:

  • UIImageView (iOS)
  • ImageView (Android)
  • <img> (Web)

Example Usage

Import:

import { Image, ImageBackground, Text } from "react-native";

const logoImg = require("./assets/adaptive-icon.png");

Static Image

<Image source={logoImg} style={{ width: 200, height: 200 }} />

Network Image

<Image
   source={{ uri: "https://picsum.photos/300" }}
   style={{ width: 200, height: 200 }}
/>

Background Image

<ImageBackground source={logoImg} style={{ flex: 1 }}>
   <Text>This is a Background Image</Text>
</ImageBackground>

ScrollView

The ScrollView component wraps the platform-specific scrolling functionality.

It supports horizontal scrolling, vertical scrolling, and nested scrolling.

Note: ScrollViews require a bounded height to function properly.

Example Usage

Import

import { View, ScrollView, Text } from "react-native";

Basic ScrollView

const logoImg = require("./assets/adaptive-icon.png");

export default function App() {
   return (
      <View style={{ flex: 1, backgroundColor: "plum", padding: 20 }}>
         <ScrollView>
            <Text>
               This is a scrollable view. Add more content here to make it
               scrollable.
            </Text>
         </ScrollView>
      </View>
   );
}

Button

The Button component allows users to trigger actions.

Button component requires a title and an onPress event handler. The onPress event handler is called when the button is pressed.

Note: The Button component has platform-specific rendering for iOS and Android.

Example Usage

Import

import { Button } from "react-native";

Basic Button

<Button title="Click Me" />

OnPress Event Handler

<Button
   title="Click Me"
   onPress={() => {
      console.log("Button Pressed");
   }}

Color Button

<Button
   title="Click Me"
   onPress={() => {
      console.log("Button Pressed");
   }}
   color="midnightblue"
/>

Disabled Button

<Button
   title="Click Me"
   onPress={() => {
      console.log("Button Pressed");
   }}
   disabled
/>

}}

Pressable

Pressable is a wrapper component that detects various stages of press interactions on its defined children.

Pressable component is a more flexible alternative to the Button component.

Pressable allows you to apply custom styles to the button.

  • onPressIn is called when a press is activated.
  • onLongPress is triggered when a press is held for longer than 500 milliseconds.
  • onPressOut is called when the press gesture is deactivated.

Pressable Interaction Flow

Example Usage

Import

import { Image, Text, Pressable } from "react-native";

Basic Pressable

<Pressable>
   <Text>Press Me</Text>
</Pressable>

OnPress Event Handler

<Pressable onPress={() => console.log("Image Pressed")}>
   <Image
      source={{ uri: "https://picsum.photos/300" }}
            style={{ width: 200, height: 200 }}
   />
</Pressable>

<Pressable onPress={() => console.log("Text Pressed")}>
   <Text>Hello, Press Here!</Text>{" "}
</Pressable>

Modal

Modal is a screen that overlays the app content to provide important information or prompt the user for a decision.

Since they are purposefully interruptive, make sure you use them only when necessary.

Example Usage

Import

import { useState } from "react";
import { View, Button, Image, Text, Pressable, Modal } from "react-native";

Basic Modal

export default function App() {
   const [isModalVisible, setIsModalVisible] = useState(false);
   return (
      <View style={{ flex: 1, backgroundColor: "plum", padding: 20 }}>
         <Button
            title="Press"
            onPress={() => setIsModalVisible(true)}
            color="midnightblue"
         />
         <Modal
            visible={isModalVisible}
            onRequestClose={() => setIsModalVisible(false)}
            animationType="slide"
         >
            <View
               style={{ flex: 1, backgroundColor: "lightblue", padding: 60 }}
            >
               <Text>Modal Content</Text>
               <Button
                  title="Close"
                  onPress={() => setIsModalVisible(false)}
                  color="midnightblue"
               />
            </View>
         </Modal>
      </View>
   );
}

StatusBar

The StatusBar component allows you to control the application's status bar.

The status bar is the zone, typically at the top of the screen, that displays the current time, Wi-Fi and network information, battery level, and other status icons.

Example Usage

This example shows how to change the status bar style and background color using the StatusBar component in React Native.

Import

import { useState } from "react";

Basic StatusBar

import { View, StatusBar } from "react-native";

export default function App() {
   return (
      <View style={{ flex: 1, backgroundColor: "plum", padding: 20 }}>
         <StatusBar
            backgroundColor="lightgreen" // Set the background color
            barStyle={"dark-content"} // Set the status bar style
            hidden // Hide the status bar
         />
      </View>
   );
}

ActivityIndicator

The ActivityIndicator component displays a circular loading indicator.

It is used to inform users about the status of ongoing processes, such as loading an app, submitting a form, or saving updates.

Example Usage

Import

import { ActivityIndicator } from "react-native";

Basic ActivityIndicator

export default function App() {
   return (
      <View style={{ flex: 1, backgroundColor: "plum", padding: 20 }}>
         <ActivityIndicator /> // Default
         <ActivityIndicator size={"large"} /> // change size
         <ActivityIndicator size={"large"} color="purple" /> // change color
         <ActivityIndicator
            size={"large"}
            color="purple"
            animating={false}
         /> // stop animation
      </View>
   );
}

Alert

Alert launches an alert dialog with specified title and message.

Optionally, you can also specify a list of buttons.

Example Usage

Import

import { View, Alert, Button } from "react-native";

Basic Alert

export default function App() {
   return (
      <View style={{ flex: 1, backgroundColor: "plum", padding: 20 }}>
         <Button
            title="Alert"
            onPress={() => Alert.alert("Invalid Password!")}
         />
      </View>
   );
}

Alert with Description

export default function App() {
   return (
      <View style={{ flex: 1, backgroundColor: "plum", padding: 20 }}>
         <Button
            title="Alert2"
            onPress={() => Alert.alert("Invalid Password!", "Try Again!")}
         />
      </View>
   );
}

Alert with custom buttons

export default function App() {
   return (
      <View style={{ flex: 1, backgroundColor: "plum", padding: 20 }}>
         <Button
            title="Alert3"
            onPress={() =>
               Alert.alert("Invalid Password!", "Try Again!", [
                  {
                     text: "Cancel",
                     onPress: () => console.log("User Pressed Canceled"),
                  },
                  { text: "OK", onPress: () => console.log("User Pressed OK") },
               ])
            }
         />
      </View>
   );
}

Custom Components

Custom components are reusable UI elements that can be created using React Native's core components.

Example Usage

[Greet] Custom Component

import { View, Text } from "react-native";
export default function Greet({ name }) {
   return (
      <View>
         <Text>Hello, {name} Welcome Back!!!</Text>
      </View>
   );
}

Using [Greet] Custom Component

import { View } from "react-native";
import Greet from "./Components/Greet";

export default function App() {
   return (
      <View style={{ flex: 1, backgroundColor: "plum", padding: 60 }}>
         <Greet name="Uday Dey" />
         <Greet name="John Doe" />
      </View>
   );
}

Styling React Native Apps

  • React Native does not use CSS for styling
  • You style your app using JavaScript
  • Names are written in camel case

Example:
backgroundColor instead of background-color

Styling Approaches

  • Inline styles
  • StyleSheet API

Inline styles

Inline styles are applied directly to the component using the style prop.

import { View } from "react-native";

export default function App() {
   return (
      <View style={{ flex: 1, backgroundColor: "plum", padding: 60 }}>
         <Text style={{ color: "blue" }}>Using Inline styles</Text>
      </View>
   );
}

StyleSheet API

StyleSheet API is used to create styles for components.

import { View, Text, StyleSheet } from "react-native";

export default function App() {
   return (
      <View style={styles.container}>
         <Text style={styles.title}>Using StyleSheet API</Text>
      </View>
   );
}

const styles = StyleSheet.create({
   container: { flex: 1, backgroundColor: "plum", padding: 60 },
   title: { color: "blue" },
});

In this section

  • StyleSheet API
  • Style various core components
  • Styling across iOS and Android

Multiple Styles

Example Usage

import { View, Text, StyleSheet } from "react-native";

export default function App() {
   return (
      <View style={styles.container}>
         <View style={[styles.box, styles.lightblueBoxBG]}>
            <Text>This is a Blue Box</Text>
         </View>
         <View style={[styles.box, styles.lightgreenBoxBG]}>
            <Text>This is a Green Box</Text>
         </View>
      </View>
   );
}

const styles = StyleSheet.create({
   container: { flex: 1, backgroundColor: "plum", padding: 60 },
   box: {
      width: 200,
      height: 200,
      padding: 60,
   },
   lightblueBoxBG: {
      backgroundColor: "lightblue",
   },
   lightgreenBoxBG: {
      backgroundColor: "lightgreen",
   },
});

Box Model

The box model is a fundamental concept in CSS that defines the layout and spacing of elements.

In React Native, the box model is used to position and style elements in the user interface.

Elements of box model are:

  • Content
  • Padding
  • Border
  • Margin

Example Usage

Import

import { View, Text, StyleSheet } from "react-native";

Basic Box Model

export default function App() {
   return (
      <View style={styles.container}>
         <View style={[styles.box, styles.lightblueBoxBG]}>
            <Text>This is a Blue Box</Text>
         </View>
         <View style={[styles.box, styles.lightgreenBoxBG]}>
            <Text>This is a Green Box</Text>
         </View>
      </View>
   );
}

const styles = StyleSheet.create({
   container: { flex: 1, backgroundColor: "plum", padding: 60 },
   box: {
      width: 200,
      height: 200,
      // padding: 60,
      paddingHorizontal: 20,
      paddingVertical: 20,
      marginVertical: 10,
      borderWidth: 2,
      borderColor: "red",
      borderRadius: 10,
   },
   lightblueBoxBG: {
      backgroundColor: "lightblue",
      justifyContent: "center",
      alignItems: "center",
   },
   lightgreenBoxBG: {
      backgroundColor: "lightgreen",
      justifyContent: "center",
      alignItems: "center",
   },
});

Shadow

There are 2 types ways to apply shadow:

  • box-shadow for iOS
  • elevation for Android

box-shadow

The box-shadow property is used to add a shadow to a view.

Properties:

  • offset-x
  • offset-y
  • blur-radius
  • spread-radius
  • color

Example Usage:

import { View, Text, StyleSheet } from "react-native";

export default function App() {
   return (
      <View style={styles.container}>
         <View style={[styles.box, styles.lightblueBoxBG, styles.boxShadow]}>
            <Text>This is a Blue Box</Text>
         </View>
            <Text>This is a Green Box</Text>
         </View>
      </View>
   );
}

const styles = StyleSheet.create({
   container: { flex: 1, backgroundColor: "plum", padding: 60 },
   box: {
      width: 200,
      height: 200,
      paddingHorizontal: 20,
      paddingVertical: 20,
      marginVertical: 10,
      borderWidth: 2,
      borderColor: "red",
      borderRadius: 10,
   },
   lightblueBoxBG: {
      backgroundColor: "lightblue",
      justifyContent: "center",
      alignItems: "center",
   },
   boxShadow: {
      shadowColor: "black",
      shadowOffset: { width: 6, height: 6 },
      shadowOpacity: 0.6,
      shadowRadius: 4,
   },
});

Elevation

The elevation property is used to add a shadow to a view.

Properties:

  • elevation
  • shadow-color

Example Usage:

import { View, Text, StyleSheet } from "react-native";

export default function App() {
   return (
      <View style={styles.container}>
         <View
            style={[styles.box, styles.lightgreenBoxBG, styles.androidShadow]}
         >
            <Text>This is a Green Box</Text>
         </View>
      </View>
   );
}

const styles = StyleSheet.create({
   container: { flex: 1, backgroundColor: "plum", padding: 60 },
   box: {
      width: 200,
      height: 200,
      // padding: 60,
      paddingHorizontal: 20,
      paddingVertical: 20,
      marginVertical: 10,
      borderWidth: 2,
      borderColor: "red",
      borderRadius: 10,
   },
   lightgreenBoxBG: {
      backgroundColor: "lightgreen",
      justifyContent: "center",
      alignItems: "center",
   },
   androidShadow: {
      elevation: 10,
      shadowColor: "darkblue",
   },
});

Layout with Flexbox

At the core of layout design in React Native is Flexbox

Flexbox is a powerful one-dimensional layout model used to arrange elements within a container

With Flexbox, you have the freedom to arrange items either horizontally (from left to right or right to left) or vertically (from top to bottom or bottom to top)

You can easily control the spacing and alignment of items within the container

Flexbox

Flexbox consists of two main entities:

  • flex container
  • flex items
<View>
   <View>Item 1</View>
   <View>Item 2</View>
   <View>Item 3</View>
</View>

Flexbox

Learning Flexbox

Much like learning CSS

Familiarize yourself with the various Flexbox properties and understand how they function

Once you grasp the concepts of Flexbox, you'll have a solid understanding of how layouts are created in React Native

flex

The flex property plays a crucial role in defining how much of a view will fill the screen along the main axis.

It accepts an integer value greater than or equal to 0, indicating the fraction of the available space the component should occupy.

Example usage

<Box style={{ backgroundColor: "blue", flex: 1 }}>Box 1</Box>
<Box style={{ backgroundColor: "green", flex: 3 }}>Box 2</Box>

flexDirection

The flexDirection property establishes the main axis, which in turn determines how the flex items are placed within the container.

By default, the main axis flows from top to bottom, causing the items to be displayed from top to bottom in the UI.

By changing the value of the flexDirection property, we can alter how the items are positioned.

Properties:

  • column : default
  • column-reverse
  • row
  • row-reverse

Example usage

const styles = StyleSheet.create({
   container: {
      flexDirection: "row",
   },
});

justifyContent

The justifyContent property controls how flex items are positioned along the main axis.

Properties:

  • flex-start
  • flex-end
  • center
  • space-between
  • space-around
  • space-evenly

Example usage

const styles = StyleSheet.create({
   container: {
      justifyContent: "space-between",
   },
});

alignItems

alignItems defines the default behavior for laying out
flex items along the container's cross-axis

Works similarly to justifyContent, but in the
perpendicular direction

Properties:

  • stretch
  • center
  • flex-start
  • flex-end
  • baseline

Baseline

baseline is the default value

Example usage

const styles = StyleSheet.create({
   container: {
      alignItems: "center",
   },
});

alignSelf

alignItems is applied to the container and controls the alignment of all items within it.

alignSelf is applied to individual items, allowing us to control the alignment of each item independently.

The values available for alignSelf are similar to those used in alignItems.

export default function App() {
   return (
      <View style={styles.container}>
         <Box style={{ backgroundColor: "blue", alignSelf: "flex-start" }}>
            Box 1
         </Box>
         <Box style={{ backgroundColor: "green", alignSelf: "flex-end" }}>
            Box 2
         </Box>
         <Box style={{ backgroundColor: "yellow", alignSelf: "center" }}>
            Box 3
         </Box>
         <Box style={{ backgroundColor: "purple", alignSelf: "stretch" }}>
            Box 4
         </Box>
         <Box style={{ backgroundColor: "orange", alignSelf: "baseline" }}>
            Box 5
         </Box>
         <Box style={{ backgroundColor: "pink", alignSelf: "auto" }}>Box 6</Box>
         <Box style={{ backgroundColor: "brown" }}>Box 7</Box>
         <Box style={{ backgroundColor: "cyan" }}>Box 8</Box>
      </View>
   );
}

const styles = StyleSheet.create({
   container: {
      flex: 1,
      alignItems: "stretch",
      marginTop: 64,
      borderWidth: 6,
      borderColor: "red",
   },
});

Flex Wrap

Flexbox allows items to wrap to the next line if there is not enough space to fit them all in the container

Properties:

  • nowrap: default
  • wrap: wrap items to the next line
  • wrap-reverse: wrap items to the previous line

Example usage

export default function App() {
   return (
      <View style={styles.container}>
         <Box style={{ backgroundColor: "blue" }}>Box 1</Box>
         <Box style={{ backgroundColor: "green" }}>Box 2</Box>
         <Box style={{ backgroundColor: "yellow" }}>Box 3</Box>
         <Box style={{ backgroundColor: "purple" }}>Box 4</Box>
         <Box style={{ backgroundColor: "orange" }}>Box 5</Box>
      </View>
   );
}

const styles = StyleSheet.create({
   container: {
      flex: 1,
      flexDirection: "row",
      flexWrap: "wrap",
      marginTop: 64,
      borderWidth: 6,
      borderColor: "red",
   },
});

alignContent

The alignContent property controls how flex items are positioned along the cross axis.

Similar to how the alignItems property aligns individual items along the cross-axis

A very important condition is that multiple columns or rows must exist within the container

Properties:

  • flex-start
  • flex-end
  • center
  • space-between
  • space-around
  • stretch

Example usage

const styles = StyleSheet.create({
   container: {
      alignContent: "stretch",
   },
});

gap

Gap related properties allow us to manage spacing between rows and columns

Properties:

  • rowGap : gap between rows
  • columnGap : gap between columns
  • gap : gap between rows and columns

Example usage

rowGap: 20,
columnGap: 10,

flexBasis

The flexBasis property determines the initial size of a flex item before any extra space in the container is distributed

Alternative to using the height and width properties in flex layouts

Properties:

  • auto: default
  • content: size based on content
  • min-content: minimum size based on content
  • max-content: maximum size based on content
  • fit-content: size based on content plus padding

Example usage

<Box style={{ backgroundColor: "orange", flexBasis: 170 }}>Box 5</Box>

flexShrink

The flexShrink property controls how flex items shrink to fit the container

Properties:

  • 0: default
  • 1
  • 2
  • 3
  • and so on

Example usage

<Box style={{ backgroundColor: "orange", flexShrink: 2 }}>Box 5</Box>

flexGrow

flexGrow determines how much space an item should occupy inside a flex container when there is extra space available.

The flexGrow factor is always relative to other items within the container.

Properties:

  • 0: default
  • 1
  • 2
  • 3
  • and so on

Example usage

<Box style={{ backgroundColor: "orange", flexGrow: 2 }}>Box 5</Box>

flex vs flexGrow

When flex is set to a positive number, it is equivalent to setting flexGrow with the same positive number.

flex also implicitly sets flexShrink to 1 and flexBasis to 0.

flex: <positive number>

flexGrow: <positive number>, flexShrink: 1, flexBasis: 0

Relative and Absolute Layout

The layouts are based on the position property, which defines how an element is positioned within its parent container:

  • relative
  • absolute

Relative layout

In this layout, an element is positioned according to the normal flow of the layout.

It remains in its original position and can be offset from that position using the top, right, bottom, and left values.

Importantly, this offset does not affect the positioning of any sibling or parent elements.

Example usage

<Box style={{ backgroundColor: "blue", top: 100, left: 100 }}>Box 1</Box>

Absolute layout

In this layout, an element does not participate in the normal flow of the layout.

It is instead laid out independently of its siblings.

The position of the element is determined by the top, right, bottom, and left values, which specify specific coordinates relative to its parent container.

Example usage

<Box
   style={{
      backgroundColor: "orange",
      position: "absolute",
      top: 300,
      left: 200,
   }}
>
   Box 2
</Box>

Dynamic User Interfaces

  • Currently, all our learning has centered around iPhone 14 and Pixel 4 devices
  • Our app's users won’t all be using identical devices
  • Device sizes may vary, ranging from more compact phones to larger devices like iPads or Android tablets
  • We must ensure that our app’s user interface remains responsive to these different device sizes while maintaining an optimal user experience
  • On the same device, a user might opt for portrait mode, while another prefers landscape orientation

Dynamic API

  • A dynamic API is an API that is responsive to the device screen size and orientation
  • A dynamic API is an API that adjusts its behavior based on the device's screen size and orientation

Example usage

import { StyleSheet, Text, View, Dimensions } from "react-native";

export default function App() {
   return (
      <View style={styles.container}>
         <View style={styles.box}>
            <Text style={styles.text}>Welcome!</Text>
         </View>
      </View>
   );
}

const windowWidth = Dimensions.get("window").width;
const windowHeight = Dimensions.get("window").height;

const styles = StyleSheet.create({
   container: {
      flex: 1,
      backgroundColor: "plum",
      alignItems: "center",
      justifyContent: "center",
   },
   box: {
      width: windowWidth > 500 ? "70%" : "90%",
      height: windowHeight > 600 ? "50%" : "90%",
      backgroundColor: "lightblue",
      alignItems: "center",
      justifyContent: "center",
      borderRadius: 10,
   },
   text: {
      fontSize: windowWidth > 500 ? 50 : 24,
      color: "black",
      fontWeight: "bold",
   },
});

Dynamic API Disadvantages

  • Dynamic UI can be complex to design and implement
  • Dynamic UI can be difficult to maintain and update
  • Dynamic UI can be difficult to test

Example usage

import { useState, useEffect } from "react";
import { StyleSheet, Text, View, Dimensions } from "react-native";

export default function App() {
   const [dimensions, setDimensions] = useState({
      window: Dimensions.get("window"),
   });

   useEffect(() => {
      const subscription = Dimensions.addEventListener(
         "change",
         ({ window }) => {
            setDimensions({ window });
         }
      );
      return () => subscription?.remove();
   });
   const { window } = dimensions;
   const windowWidth = window.width;
   const windowHeight = window.height;

   return (
      <View style={styles.container}>
         <View
            style={[
               styles.box,
               {
                  width: windowWidth > 500 ? "70%" : "90%",
                  height: windowHeight > 600 ? "50%" : "90%",
               },
            ]}
         >
            <Text style={{ fontSize: windowWidth > 500 ? 50 : 24 }}>
               Welcome!
            </Text>
         </View>
      </View>
   );
}

const styles = StyleSheet.create({
   container: {
      flex: 1,
      backgroundColor: "plum",
      alignItems: "center",
      justifyContent: "center",
   },
   box: {
      backgroundColor: "lightblue",
      alignItems: "center",
      justifyContent: "center",
      borderRadius: 10,
   },
});

UseWindowDimensions

UseWindowDimensions is a hook that returns the dimensions of the window in pixels.

It is better to use UseWindowDimensions than Dimensions.get("window")

Example usage

import { Text, View } from "react-native";
import { useWindowDimensions } from "react-native";

export default function App() {
   const { width, height } = useWindowDimensions();
   return (
      <View>
         <Text>Width: {width}</Text>
         <Text>Height: {height}</Text>
      </View>
   );
}

safeAreaView

safeAreaView is a component that ensures that the content of the app is not covered by the status bar, navigation bar, or other elements that may be present on the screen.

Example usage

import { SafeAreaView, Text } from "react-native";

export default function App() {
   return (
      <SafeAreaView>
         <Text>SafeAreaView</Text>
      </SafeAreaView>
   );
}

Platform Specific Code

  • When developing a cross-platform app, maximizing code reuse is a priority
  • There are situations where it becomes necessary to tailor your code to specific platforms
  • React Native offers two approaches for organizing and separating platform-specific code:
    • Platform module
    • Platform-specific file extensions

Properties:

  • Platform.OS: A string that represents the current platform.
  • Platform.Version: A number that represents the current platform version.
  • Platform.select: A function that returns a value based on the current platform.
  • Platform.OSX: A string that represents the macOS platform.
  • Platform.IOS: A string that represents the iOS platform.
  • Platform.ANDROID: A string that represents the Android platform.
  • Platform.WEB: A string that represents the web platform.

Platform Specific File Extensions

  • *.ios.js: JavaScript file that is specific to the iOS platform
  • *.android.js: JavaScript file that is specific to the Android platform
  • *.web.js: JavaScript file that is specific to the web platform

Example usage

Platform.OS

import { Platform } from "react-native";

   container: {
      flex: 1,
      backgroundColor: "plum",
      paddingTop: Platform.OS === "android" ? 25 : 0,
   },

Platform.select

   text: {
      ...Platform.select({
         ios: {
            color: "purple",
            fontSize: 24,
            fontStyle: "italic",
         },
         android: {
            color: "blue",
            fontSize: 30,
         },
      }),

      fontWeight: "bold",
      textAlign: "center",
   },

List

The List component is a component that displays a list of items in a scrollable view.

FlatList

FlatList is used to render large lists of data efficiently.


🧱 data

The source of items to render in the list.

Example:

const data = [
   { id: "1", title: "Item 1" },
   { id: "2", title: "Item 2" },
];

🧱 renderItem

Function that takes an item from data and renders it.

Example:

renderItem={({ item }) => <Text>{item.title}</Text>}

🧱 keyExtractor

Function to extract a unique key for each item.

Example:

keyExtractor={(item) => item.id}

🧱 ListHeaderComponent

Component rendered at the top of the list.

Example:

ListHeaderComponent={<Text>Header</Text>}

🧱 ListFooterComponent

Component rendered at the bottom of the list.

Example:

ListFooterComponent={<Text>Footer</Text>}

🧱 ItemSeparatorComponent

Component rendered between items.

Example:

ItemSeparatorComponent={() => <View style={{ height: 1, backgroundColor: 'gray' }} />}

🧱 horizontal

Renders list horizontally instead of vertically.

Example:

horizontal={true}

🧱 numColumns

How many columns to render.

Example:

numColumns={2}

🧱 onEndReached

Called when the end of the list is reached.

Example:

onEndReached={() => console.log('Reached end')}

🧱 onRefresh & refreshing

Used for pull-to-refresh functionality.

Example:

const [refreshing, setRefreshing] = useState(false);

const onRefresh = () => {
   setRefreshing(true);
   // Fetch data here
   setTimeout(() => setRefreshing(false), 2000);
};

<FlatList
   data={data}
   renderItem={renderItem}
   keyExtractor={keyExtractor}
   onRefresh={onRefresh}
   refreshing={refreshing}
/>;

🧱 initialNumToRender

Number of items to render initially.

Example:

initialNumToRender={10}

🧱 maxToRenderPerBatch

Maximum number of items to render per batch.

Example:

maxToRenderPerBatch={5}

🧱 windowSize

Number of viewable screens worth of content to render ahead.

Example:

windowSize={10}

🧱 getItemLayout

Improves performance by specifying item height.

Example:

getItemLayout={(data, index) => (
  { length: 50, offset: 50 * index, index }
)}

📋 SectionList

SectionList is used to render grouped lists of data, section by section.


🧱 sections

An array of sections, each with a title and data.

Example:

const sections = [
   { title: "Fruits", data: ["Apple", "Banana"] },
   { title: "Vegetables", data: ["Carrot", "Tomato"] },
];

🧱 renderItem

Function that renders each item.

Example:

renderItem={({ item }) => <Text>{item}</Text>}

🧱 renderSectionHeader

Function that renders the header for each section.

Example:

renderSectionHeader={({ section: { title } }) => (
  <Text style={{ fontWeight: 'bold' }}>{title}</Text>
)}

🧱 keyExtractor

Function to extract a unique key for each item.

Example:

keyExtractor={(item, index) => item + index}

🧱 ListHeaderComponent

Component rendered at the top of the list.

Example:

ListHeaderComponent={<Text>Header</Text>}

🧱 ListFooterComponent

Component rendered at the bottom of the list.

Example:

ListFooterComponent={<Text>Footer</Text>}

🧱 ItemSeparatorComponent

Component rendered between items.

Example:

ItemSeparatorComponent={() => <View style={{ height: 1, backgroundColor: 'gray' }} />}

🧱 SectionSeparatorComponent

Component rendered between sections.

Example:

SectionSeparatorComponent={() => <View style={{ height: 10 }} />}

🧱 stickySectionHeadersEnabled

Enables sticky section headers.

Example:

stickySectionHeadersEnabled={true}

🧱 onEndReached

Called when the end of the list is reached.

Example:

onEndReached={() => console.log('End reached')}

🧱 onRefresh & refreshing

Used for pull-to-refresh functionality.

Example:

const [refreshing, setRefreshing] = useState(false);

const onRefresh = () => {
   setRefreshing(true);
   // Fetch data here
   setTimeout(() => setRefreshing(false), 2000);
};

<SectionList
   sections={sections}
   renderItem={renderItem}
   renderSectionHeader={renderSectionHeader}
   keyExtractor={keyExtractor}
   onRefresh={onRefresh}
   refreshing={refreshing}
/>;

🧱 initialNumToRender

Number of items to render initially.

Example:

initialNumToRender={5}

🧱 getItemLayout

Improves performance by specifying item height.

Example:

getItemLayout={(data, index) => (
  { length: 50, offset: 50 * index, index }
)}

Inputs in RN vs. Web

In web development, we have lots of HTML elements at our disposal to capture user input.

For example, input fields, text areas, dropdown menus, checkboxes, radio groups, and many more.

The core RN (React Native) library only provides TextInput and Switch.

These will be the sole focus of our learning in this section.

"What about the other components?"

Expo expands our toolkit, offering additional components like checkboxes and date pickers through the Expo SDK.


TextInput

TextInput component is a fundamental building block for user input in React Native

It allows users to enter text and other data into your app

Props:

  • defaultValue: Sets the initial value of the input field
  • value: Controls the current value of the input field
  • onChangeText: Called when the text inside the input field changes
  • onChange: Called when the value of the input field changes
  • placeholder: Text displayed when the input field is empty
  • multiline: Whether to allow multiple lines of text
  • onSubmitEditing: Called when the user finishes editing the input field
  • onEndEditing: Called when the user stops editing the input field
  • onFocus: Called when the input field gains focus
  • onBlur: Called when the input field loses focus
  • placeholderTextColor: Color of the placeholder text
  • keyboardType: Type of keyboard to use
  • returnKeyType: Type of return key to use
  • secureTextEntry: Whether to hide the text entered in the input field
  • numberOfLines: Maximum number of lines to display
  • editable: Whether the input field is editable
  • selectTextOnFocus: Whether to select the text when the input field gains focus
  • clearTextOnFocus: Whether to clear the text when the input field gains focus
  • maxLength: Maximum length of the input field

Example:

<TextInput
   value={name}
   onChangeText={(text) => setName(text)}
   style={styles.input}
   placeholder="Enter your name"
   autoCapitalize="none"
   autoCorrect={false}
   secureTextEntry
   multiline
   placeholderTextColor="gray"
   numberOfLines={4}
   editable={false}
   selectTextOnFocus
   clearTextOnFocus
   maxLength={10}
   returnKeyType="done"
   returnKeyLabel="Done"
   onSubmitEditing={() => console.log("Submitted")}
   onEndEditing={() => console.log("Editing ended")}
   onFocus={() => console.log("Focused")}
   onBlur={() => console.log("Blurred")}
   keyboardType="numeric"
   keyboardAppearance="dark"
   textContentType="oneTimeCode"
   enablesReturnKeyAutomatically
   blurOnSubmit
   selectTextOnFocus
   clearTextOnFocus
/>

Switch

The Switch component serves as a valuable tool for integrating toggles into your app's user interface

It's particularly well-suited for scenarios where you require users to make binary choices, such as enabling or disabling specific app features.

Props:

  • value: The current state of the switch (true for on, false for off)
  • onValueChange: Called when the switch's state changes
  • disabled: Whether the switch is disabled
  • disabledThumbColor: Color of the thumb when the switch is disabled
  • disabledTrackColor: Color of the track when the switch is disabled
  • trackColor: Color of the track
  • thumbColor: Color of the thumb
  • trackBorderColor: Color of the track border
  • thumbBorderColor: Color of the thumb border
  • trackBorderWidth: Width of the track border
  • thumbBorderWidth: Width of the thumb border
  • thumbSize: Size of the thumb
  • trackSize: Size of the track
  • ios_backgroundColor: Background color for iOS
  • ios_tintColor: Tint color for iOS
  • ios_thumbTintColor: Thumb tint color for iOS

Example:

<Switch
   value={isEnabled}
   onValueChange={toggleSwitch}
   disabled={false}
   disabledThumbColor="gray"
   disabledTrackColor="gray"
   trackColor={{ false: "#767577", true: "#81b0ff" }}
   thumbColor={isEnabled ? "#f5dd4b" : "#f4f3f4"}
   trackBorderColor="gray"
   thumbBorderColor="gray"
   trackBorderWidth={2}
   thumbBorderWidth={2}
   thumbSize={20}
   trackSize={40}
   ios_backgroundColor="#3e3e3e"
   ios_tintColor="#3e3e3e"
   ios_thumbTintColor="#3e3e3e"
/>

Forms

Managing Form State: Controlling the dynamic data within our forms

Handling Form Validation: Ensuring the integrity and validity of the user's input

Displaying Validation Messages: Communicating the results of validation to the user

Submitting Form Data: Sending the collected information where it needs to go

Login Form

  • email: Email address
  • password: Password

Example:

const [email, setEmail] = useState("");
const [password, setPassword] = useState("");

const handleLogin = () => {
   // Perform login logic here
   console.log("Email:", email);
   console.log("Password:", password);
};

KeyboardAvoidingView

  • behavior: Keyboard behavior (e.g., "position", "height", "padding")
  • keyboardVerticalOffset: Vertical offset for the keyboard

Example:

return (
   <KeyboardAvoidingView
      behavior="padding"
      keyboardVerticalOffset={Platform.OS === "ios" ? 100 : 0}
      style={styles.container}
   >
      <View style={styles.form}>
         <Image
            source={require("./assets/adaptive-icon.png")}
            style={styles.logo}
         />
         <Text style={styles.label}>Username</Text>
         <TextInput
            style={styles.input}
            placeholder="Enter your username"
            value={username}
            onChangeText={setUsername}
         />
         {errors.username ? (
            <Text style={styles.errorText}> {errors.username}</Text>
         ) : null}
         <Text style={styles.label}>Password</Text>
         <TextInput
            style={styles.input}
            placeholder="Enter your password"
            secureTextEntry
            autoCapitalize="none"
            autoCorrect={false}
            value={password}
            onChangeText={setPassword}
         />
         {errors.password ? (
            <Text style={styles.errorText}> {errors.password}</Text>
         ) : null}
         <Button title="Login" onPress={handleSubmit} />
      </View>
   </KeyboardAvoidingView>
);

Form Validation

Form validation is a crucial step in ensuring the accuracy and integrity of user input. It involves evaluating the data entered by the user and ensuring that it meets the specified criteria.


Example:

export default function App() {
   const [username, setUsername] = useState("");
   const [password, setPassword] = useState("");
   const [errors, setErrors] = useState({});

   const validateForm = () => {
      let errors = {};

      if (!username) errors.username = "Username is required";
      if (!password) errors.password = "Password is required";

      setErrors(errors);

      return Object.keys(errors).length === 0;
   };

   return (
      <Text style={styles.label}>Username</Text>
<TextInput/>

{
   errors.username ? (
   <Text style={styles.errorText}> {errors.username}</Text>
) : null
}

<Text style={styles.label}>Password</Text>
<TextInput/>

{
   errors.password ? (
   <Text style={styles.errorText}> {errors.password}</Text>
) : null
}
<Button title="Login" onPress={validateForm} />
)

Form Submission

Form submission is the process of sending the collected data to a server or other destination for processing or storage.


Example:

const handleSubmit = () => {
   if (validateForm()) {
      console.log("Submitted", username, password);
      setTimeout(() => {
         setUsername("");
         setPassword("");
         setErrors({});
      }, 0);
   }
};

<Button title="Login" onPress={handleSubmit} />;

Networking

  • Fetching and submitting data to an API
  • Loading states
  • Error handling
  • FlatList component to display our data

GET Requests

GET requests are used to retrieve data from a server.


Parameters:

  • url: The URL of the API endpoint
  • headers: Optional headers to include in the request
  • params: Optional query parameters to include in the request

Example:

import { useEffect, useState } from "react";

export default function App() {
   const [postList, setPostList] = useState([]);

   const fetchData = async (limit = 10) => {
      const Response = await fetch(
         `https://jsonplaceholder.typicode.com/posts?_limit=${limit}`
      );
      const data = await Response.json();
      setPostList(data);
   };

   useEffect(() => {
      fetchData();
   }, []);

   return (
      <SafeAreaView style={styles.container}>
         <View style={styles.listContainer}>
            <FlatList
               data={postList}
               renderItem={({ item }) => {
                  return (
                     <View style={styles.card}>
                        <Text style={styles.titleText}>{item.title}</Text>
                        <Text style={styles.bodyText}>{item.body}</Text>
                     </View>
                  );
               }}
               ItemSeparatorComponent={() => <View style={{ height: 16 }} />}
               ListEmptyComponent={<Text>No Post Found</Text>}
               ListHeaderComponent={
                  <Text style={styles.headerText}>Post List</Text>
               }
               ListFooterComponent={
                  <Text style={styles.footerText}>End of List</Text>
               }
            />
         </View>
      </SafeAreaView>
   );
}

Loading State

The loading state is used to indicate that data is being fetched from the server.


Parameters:

  • isLoading: A boolean value indicating whether data is being fetched from the server

Example:

const [IsLoading, setIsLoading] = useState(true);
const fetchData = async (limit = 10) => {
   const Response = await fetch(
      `https://jsonplaceholder.typicode.com/posts?_limit=${limit}`
   );
   const data = await Response.json();
   setPostList(data);
   setIsLoading(false);
};
if (IsLoading) {
   return (
      <SafeAreaView style={styles.loadingContainer}>
         <ActivityIndicator size="large" color={"blue"} />
         <Text>Loading...</Text>
      </SafeAreaView>
   );
}

Post Requests

Post requests are used to submit data to a server.


Parameters:

  • url: The URL of the API endpoint
  • headers: Optional headers to include in the request
  • body: Optional body to include in the request

Example:

export default function App() {
   const [postList, setPostList] = useState([]);
   const [IsLoading, setIsLoading] = useState(true);
   const [refreshing, setRefreshing] = useState(false);
   const [postBody, setPostBody] = useState("");
   const [postTitle, setPostTitle] = useState("");
   const [isPosting, setIsPosting] = useState(false);

   const fetchData = async (limit = 10) => {
      const Response = await fetch(
         `https://jsonplaceholder.typicode.com/posts?_limit=${limit}`
      );
      const data = await Response.json();
      setPostList(data);
      setIsLoading(false);
   };

   const addPost = async () => {
      setIsPosting(true);
      const Response = await fetch(
         "https://jsonplaceholder.typicode.com/posts",
         {
            method: "POST",
            headers: {
               "Content-Type": "application/json",
            },
            body: JSON.stringify({
               title: postTitle,
               body: postBody,
            }),
         }
      );
      const newPost = await Response.json();
      setPostList([newPost, ...postList]);
      setPostTitle("");
      setPostBody("");
      setIsPosting(false);
   };

   return (
      <SafeAreaView style={styles.container}>
         <>
            <View style={styles.inputContainer}>
               <TextInput
                  style={styles.input}
                  placeholder="Post Title"
                  value={postTitle}
                  onChangeText={setPostTitle}
               />
               <TextInput
                  style={styles.input}
                  placeholder="Post Body"
                  value={postBody}
                  onChangeText={setPostBody}
               />
               <Button
                  title={isPosting ? "Posting..." : "Add Post"}
                  onPress={addPost}
                  disabled={isPosting}
               />
            </View>
         </>
      </SafeAreaView>
   );
}

Error Handling

Error handling is used to handle errors that occur during the execution of a program.


Example:

import {
   SafeAreaView,
   StatusBar,
   StyleSheet,
   View,
   FlatList,
   Text,
   ActivityIndicator,
   TextInput,
   Button,
} from "react-native";
import { useEffect, useState } from "react";

export default function App() {
   const [postList, setPostList] = useState([]);
   const [IsLoading, setIsLoading] = useState(true);
   const [refreshing, setRefreshing] = useState(false);
   const [postBody, setPostBody] = useState("");
   const [postTitle, setPostTitle] = useState("");
   const [isPosting, setIsPosting] = useState(false);
   const [error, setError] = useState("");

   const fetchData = async (limit = 10) => {
      try {
         const Response = await fetch(
            `https://jsonplaceholder.typicode.com/posts?_limit=${limit}`
         );
         const data = await Response.json();
         setPostList(data);
         setIsLoading(false);
         setError("");
      } catch (error) {
         console.error("Error fetching data:", error);
         setIsLoading(false);
         setError("Failed to fetch post list");
      }
   };

   const addPost = async () => {
      setIsPosting(true);
      try {
         const Response = await fetch(
            "https://jsonplaceholder.typicode.com/posts",
            {
               method: "POST",
               headers: {
                  "Content-Type": "application/json",
               },
               body: JSON.stringify({
                  title: postTitle,
                  body: postBody,
               }),
            }
         );
         const newPost = await Response.json();
         setPostList([newPost, ...postList]);
         setPostTitle("");
         setPostBody("");
         setIsPosting(false);
         setError("");
      } catch (error) {
         console.error("Error posting data:", error);
         setIsPosting(false);
         setError("Failed to add new post");
      }
   };

   if (IsLoading) {
      return (
         <SafeAreaView style={styles.loadingContainer}>
            <ActivityIndicator size="large" color={"blue"} />
            <Text>Loading...</Text>
         </SafeAreaView>
      );
   }

   return (
      <SafeAreaView style={styles.container}>
         {error ? (
            <View style={styles.errorContainer}>
               <Text style={styles.errorText}>{error}</Text>
            </View>
         ) : (
            <>
               <View style={styles.inputContainer}>
                  <TextInput
                     style={styles.input}
                     placeholder="Post Title"
                     value={postTitle}
                     onChangeText={setPostTitle}
                  />
                  <TextInput
                     style={styles.input}
                     placeholder="Post Body"
                     value={postBody}
                     onChangeText={setPostBody}
                  />
                  <Button
                     title={isPosting ? "Posting..." : "Add Post"}
                     onPress={addPost}
                     disabled={isPosting}
                  />
               </View>
               <View style={styles.listContainer}>
                  <FlatList
                     data={postList}
                     renderItem={({ item }) => {
                        return (
                           <View style={styles.card}>
                              <Text style={styles.titleText}>{item.title}</Text>
                              <Text style={styles.bodyText}>{item.body}</Text>
                           </View>
                        );
                     }}
                     ItemSeparatorComponent={() => (
                        <View style={{ height: 16 }} />
                     )}
                     ListEmptyComponent={<Text>No Post Found</Text>}
                     ListHeaderComponent={
                        <Text style={styles.headerText}>Post List</Text>
                     }
                     ListFooterComponent={
                        <Text style={styles.footerText}>End of List</Text>
                     }
                     refreshing={refreshing}
                     onRefresh={handleRefresh}
                  />
               </View>
            </>
         )}
      </SafeAreaView>
   );
}

Navigation

  • The mechanism that allows users to move across different screens, access features, and generally use your app effectively
  • A go-to solution for handling navigation is the React Navigation library
  • Expo has its own built-in routing feature exclusive to Expo projects
  • React Navigation works both with and without Expo in React Native apps
  • Focus on React Navigation in this section

React Navigation

  • Provides a variety of navigators like Stack, Drawer, and Tab navigators
  • Stack Navigator provides a way for your app to transition between screens where each new screen is placed on top of a stack
  • Drawer Navigator renders a navigation drawer on the side of the screen which can be opened and closed via gestures
  • Tab Navigator at the bottom of your screen lets you easily switch between different routes

Dependencies

"react-navigation": "^4.4.4",
"react-navigation-stack": "^2.10.4",
"react-navigation-drawer": "^2.7.1",
"react-navigation-tabs": "^2.11.2",

Dependencies Code Snippet

npm install @react-navigation/native

npx expo install react-native-screens react-native-safe-area-context

npm install @react-navigation/native-stack

Basic Usage

import { NavigationContainer } from "@react-navigation/native";

export default function App() {
   return <NavigationContainer></NavigationContainer>;
}

Stack Navigation

Each new screen is stacked on top of the previous one like a deck of cards.

  • When navigating to a new screen, a new card is placed on top of the stack.
  • When navigating back, the top card is removed, revealing the previous screen.

This approach allows users to drill down into detailed views and then retrace their steps when done.

It is particularly useful in scenarios where a linear flow of screens is required.

Example Navigation Flow:

List View → Details View → More Details View

Two navigators: Stack Navigator and Native Stack Navigator

The Stack Navigator is a JavaScript-based navigator which offers a high degree of customization,
making it a great choice for apps that require a unique navigation experience.

However, this comes at the cost of performance — especially when compared to its counterpart,
the Native Stack Navigator.

The Native Stack Navigator leverages the native navigation constructs of iOS and Android,
providing better performance and a more native feel to the transitions and gestures.

The caveat here is, it might not offer the same level of customization as the Stack Navigator.


Example:

import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import Home_Screen from "./screens/Home_Screen";
import About_Screen from "./screens/About_Screen";

const Stack = createNativeStackNavigator();

export default function App() {
   return (
      <NavigationContainer>
         <Stack.Navigator initialRouteName="About">
            <Stack.Screen name="Home" component={Home_Screen} />
            <Stack.Screen name="About" component={About_Screen} />
         </Stack.Navigator>
      </NavigationContainer>
   );
}

Navigating Between Screens

We can navigate to a new screen by calling the navigate method on the navigator instance.

Methods:

  • navigate(name, params): Navigates to the screen with the given name and optional parameters.
  • goBack(): Navigates back to the previous screen.
  • replace(name, params): Replaces the current screen with the screen with the given name and optional parameters.
  • reset(state): Resets the navigation state to the given state.
  • pop(): Navigates back to the previous screen.
  • popToTop(): Navigates back to the root screen.

Example:

App.js

import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import Home_Screen from "./screens/Home_Screen";
import About_Screen from "./screens/About_Screen";

const Stack = createNativeStackNavigator();

export default function App() {
   return (
      <NavigationContainer>
         <Stack.Navigator initialRouteName="Home">
            <Stack.Screen name="Home" component={Home_Screen} />
            <Stack.Screen name="About" component={About_Screen} />
         </Stack.Navigator>
      </NavigationContainer>
   );
}

Home_Screen.js

Using the navigation method to navigate to a new screen:

import { View, Text, StyleSheet, Button } from "react-native";

export default function Home_Screen({ navigation }) {
   return (
      <View style={styles.container}>
         <Text style={styles.text}>Home Screen</Text>
         <Button
            title="Go to About"
            onPress={() => navigation.navigate("About")}
         />
      </View>
   );
}

About_Screen.js

Using the useNavigation hook to navigate to a new screen:

import { View, Text, StyleSheet, Button } from "react-native";
import { useNavigation } from "@react-navigation/native";

export default function About_Screen() {
   const navigation = useNavigation();

   return (
      <View style={styles.container}>
         <Text style={styles.text}>About Screen</Text>
         <Button
            title="Go to Home"
            onPress={() => navigation.navigate("Home")}
         />
      </View>
   );
}

Passing Data Between Screens

We can pass data between screens using the params property of the navigate method.

Example:

Home_Screen.js

import { View, Text, StyleSheet, Button } from "react-native";

export default function Home_Screen({ navigation, route }) {
   return (
      <View style={styles.container}>
         <Text style={styles.text}>Home Screen</Text>
         <Button
            title="Go to About"
            onPress={() => navigation.navigate("About", { name: "Luffy" })}
         />
         <Text style={styles.text}>Result: {route.params?.result}</Text>
      </View>
   );
}

About_Screen.js

import { View, Text, StyleSheet, Button } from "react-native";

export default function About_Screen({ navigation, route }) {
   const { name } = route.params;

   return (
      <View style={styles.container}>
         <Text style={styles.text}>About {name}</Text>
         <Button
            title="Update the name"
            onPress={() => navigation.setParams({ name: "Sanji" })}
         />
         <Button
            title="Go back with data"
            onPress={() => {
               navigation.navigate("Home", { result: "Data from About" });
            }}
         />
      </View>
   );
}

Stack Navigation Options

We can set navigation options for each screen using the options property of the Stack.Screen component.


Methods:

  • title: Sets the title of the screen.
  • headerStyle: Sets the style of the header.
  • headerTitle: Sets the title of the header.
  • headerTitleAlign: Sets the alignment of the title in the header.
  • headerTitleStyle: Sets the style of the title in the header.
  • headerBackTitle: Sets the title of the back button in the header.
  • headerBackTitleVisible: Sets whether the back button title is visible in the header.
  • headerBackTitleStyle: Sets the style of the back button title in the header.
  • headerBackVisible: Sets whether the back button is visible in the header.
  • headerTintColor: Sets the color of the header.
  • headerRight: Sets the right component of the header.
  • headerLeft: Sets the left component of the header.
  • headerRightContainerStyle: Sets the style of the right container in the header.
  • headerLeftContainerStyle: Sets the style of the left container in the header.
  • headerRightStyle: Sets the style of the right component in the header.
  • headerLeftStyle: Sets the style of the left component in the header.
  • contentStyle: Sets the style of the content in the screen.

Example:

App.js

import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import Home_Screen from "./screens/Home_Screen";
import About_Screen from "./screens/About_Screen";
import { Pressable, Text } from "react-native";

const Stack = createNativeStackNavigator();

export default function App() {
   return (
      <NavigationContainer>
         <Stack.Navigator
            initialRouteName="Home"
            screenOptions={{
               headerStyle: { backgroundColor: "#6a51ae" },
               headerTintColor: "#fff",
               headerTitleStyle: { fontWeight: "bold" },
               headerTitleAlign: "center",
               headerRight: () => (
                  <Pressable onPress={() => alert("Menu button pressed!")}>
                     <Text style={{ color: "white", fontSize: 20 }}>Menu</Text>
                  </Pressable>
               ),
               contentStyle: { backgroundColor: "#e8e4f3" },
            }}
         >
            <Stack.Screen
               name="Home"
               component={Home_Screen}
               options={{
                  title: "Welcome Home!",
               }}
            />
            <Stack.Screen name="About" component={About_Screen} />
         </Stack.Navigator>
      </NavigationContainer>
   );
}

This applies the navigation options to both the Home and About screens.


If we want to apply the navigation options to only the Home screen, we can do the following:

<Stack.Screen
   name="Home"
   component={Home_Screen}
   options={{
      title: "Welcome Home!",
      headerStyle: { backgroundColor: "#6a51ae" },
      headerTintColor: "#fff",
      headerTitleStyle: { fontWeight: "bold" },
      headerTitleAlign: "center",
   }}

Static & Dynamic Stack Navigator Options

We can set navigation options for each screen using the options property of the Stack.Screen component.

We can also set navigation options for the entire navigator using the screenOptions property of the Stack.Navigator component.


Example:

App.js

<Stack.Screen
   name="Home"
   component={Home_Screen}
   options={{
      title: "Welcome Home!",
   }}
/>
<Stack.Screen
   name="About"
   component={About_Screen}
   initialParams={{
      name: "Guest",
   }}
   /* options={({ route }) => ({
      title: route.params.name,
   })} */
/>

About_Screen.js

export default function About_Screen({ navigation, route }) {
   const { name } = route.params;

   useLayoutEffect(() => {
      navigation.setOptions({
         title: name,
      });
   }, [navigation, name]);

   return (
      <View style={styles.container}>
         <Text style={styles.text}>About {name}</Text>
         <Button
            title="Update the name"
            onPress={() => navigation.setParams({ name: "Sanji" })}
         />
         <Button
            title="Go back with data"
            onPress={() => {
               navigation.navigate("Home", { result: "Data from About" });
            }}
         />
      </View>
   );
}

Home_Screen.js

export default function Home_Screen({ navigation, route }) {
   return (
      <View style={styles.container}>
         <Text style={styles.text}>Home Screen</Text>
         <Text style={styles.text}>Result: {route.params?.result}</Text>
         <Button
            title="Go to About"
            onPress={() =>
               navigation.navigate("About", {
                  name: "Luffy",
               })
            }
         />
      </View>
   );
}

Drawer Navigation

Drawer Navigator introduces a hidden menu, sliding from either side of the screen.

It is particularly beneficial in apps with multiple main sections that require a neat and organized navigation structure.

Dependencies:

npm install @react-navigation/drawer

npx expo install react-native-gesture-handler react-native-reanimated

Imports:

import "react-native-gesture-handler";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/drawer";

const Drawer = createNativeStackNavigator();

Reanimated Plugin:

https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/


babel.config.js

Add the plugin to your babel.config.js file:

 plugins: [
      ...
      'react-native-reanimated/plugin',
    ],

Like This:

module.exports = function (api) {
   api.cache(true);
   return {
      presets: ["babel-preset-expo"],
      plugins: ["react-native-reanimated/plugin"],
   };
};

Next we Need to Clear the cache:

Add the following command to your package.json file:

npx expo start -c

Like This:

"scripts": {
   "start": "expo start -c", // <-- Here
   "android": "expo start --android",
   "ios": "expo start --ios",
   "web": "expo start --web"
},

Example:

App.js

import "react-native-gesture-handler";
import { NavigationContainer } from "@react-navigation/native";
import { createDrawerNavigator } from "@react-navigation/drawer";
import DashboardScreen from "./screens/DashboardScreen";
import SettingsScreen from "./screens/SettingsScreen";

const Drawer = createDrawerNavigator();

export default function App() {
   return (
      <NavigationContainer>
         <Drawer.Navigator>
            <Drawer.Screen name="Dashboard" component={DashboardScreen} />
            <Drawer.Screen name="Settings" component={SettingsScreen} />
         </Drawer.Navigator>
      </NavigationContainer>
   );
}

DashboardScreen.js

import { View, Text, StyleSheet } from "react-native";

const DashboardScreen = () => {
   return (
      <View style={styles.container}>
         <Text style={styles.text}>DashboardScreen</Text>
      </View>
   );
};

export default DashboardScreen;

SettingsScreen.js

import { View, Text, StyleSheet } from "react-native";

const SettingsScreen = () => {
   return (
      <View style={styles.container}>
         <Text style={styles.text}>SettingsScreen</Text>
      </View>
   );
};

export default SettingsScreen;

More Examples:

Remember to destructure the navigation object:

const DashboardScreen = ({ navigation }) => {
   return (
      ...
   )
}

To Open the Drawer

<Button title="Toggle Drawer" onPress={() => navigation.toggleDrawer()} />

To Navigate to a Screen

<Button title="Go to Settings" onPress={() => navigation.jumpTo("Settings")} />

To Navigate Back

<Button title="Go Back" onPress={() => navigation.goBack()} />

To Navigate Back to the First Screen

<Button title="Go Back to First Screen" onPress={() => navigation.popToTop()} />

Drawer Navigation Options

Drawer Navigator allows you to set screen-specific options using the options prop in each Drawer.Screen.


Example:

This are the 5 most used options in drawer navigation:

<Drawer.Screen
   name="Dashboard"
   component={DashboardScreen}
   options={{
      title: "My Dashboard",
      drawerLabel: "Dashboard Label",
      drawerActiveTintColor: "#333",
      drawerActiveBackgroundColor: "lightblue",
      drawerContentStyle: {
         backgroundColor: "#c6cbef",
      },
   }}
/>

You can find more at: https://reactnavigation.org/docs/drawer-navigator


Common Header Options (via options prop):

Option Description
title Title for the screen (also used as drawer label if drawerLabel is absent).
headerShown Show or hide the header.
headerStyle Style for the header container.
headerTitleStyle Style for the header title text.
headerTitleAlign Align header title: "left" or "center".
headerTitle Custom title or component.
headerTintColor Color for back button and title text.
headerTransparent Make the header background transparent.
headerBackVisible Show/hide the back button.
headerBackTitleVisible Show/hide the back title (iOS only).
headerBackTitle Set custom text for the back button (iOS only).
headerBackTitleStyle Style for back button title (iOS only).
headerBackAccessibilityLabel Accessibility label for back button.
headerBackAccessibilityHint Accessibility hint for back button.
headerLeft Component/function for left side of header.
headerRight Component/function for right side of header.
headerLeftContainerStyle Style for the left container.
headerRightContainerStyle Style for the right container.
headerBackground Custom background component.
headerShadowVisible Show/hide header shadow (iOS and Android).
headerBlurEffect Apply blur effect (iOS only).

Drawer-Specific Options (Drawer.Screen → options)

Option Description
drawerLabel Label to show in the drawer. Defaults to title or name prop.
drawerIcon Function that returns an icon to display in the drawer.
drawerActiveTintColor Color for the label and icon of the active item.
drawerInactiveTintColor Color for the label and icon of the inactive items.
drawerActiveBackgroundColor Background color for the active drawer item.
drawerInactiveBackgroundColor Background color for inactive drawer items.
drawerLabelStyle Style object for the drawer label.
drawerItemStyle Style object for the individual drawer item container.
drawerType (in Drawer.Navigator) Animation type of the drawer: front, back, slide, or permanent.

Tab Navigation

Tab Navigator introduces a row of tabs (usually at the bottom) to switch between top-level screens.

It’s ideal for apps with a small number of main sections that users switch between frequently.

Dependencies:

npm install @react-navigation/bottom-tabs

npx expo install react-native-gesture-handler react-native-reanimated

Imports:

import { NavigationContainer } from "@react-navigation/native";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";

const Tab = createBottomTabNavigator();

Reanimated Plugin:

https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/


If you’re using Reanimated-based animations, add to babel.config.js:

babel.config.js

 plugins: [
      ...
      'react-native-reanimated/plugin',
    ],

Like This:

module.exports = function (api) {
   api.cache(true);
   return {
      presets: ["babel-preset-expo"],
      plugins: ["react-native-reanimated/plugin"],
   };
};

Next we Need to Clear the cache:

Ensure to clear Metro’s cache for Reanimated changes to take effect.

Add the following command to your package.json file:

npx expo start -c

Like This:

"scripts": {
   "start": "expo start -c", // <-- Here
   "android": "expo start --android",
   "ios": "expo start --ios",
   "web": "expo start --web"
},

Example:

Basic Structure:

App.js

import { NavigationContainer } from "@react-navigation/native";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import ProfileScreen from "./screens/ProfileScreen";
import CourseListScreen from "./screens/CourseListScreen";
import SettingsScreen from "./screens/SettingsScreen";

const Tab = createBottomTabNavigator();

export default function App() {
   return (
      <NavigationContainer>
         <Tab.Navigator>
            <Tab.Screen name="Profile" component={ProfileScreen} />
            <Tab.Screen name="Course List" component={CourseListScreen} />
            <Tab.Screen name="Settings" component={SettingsScreen} />
         </Tab.Navigator>
      </NavigationContainer>
   );
}

ProfileScreen.js | CourseListScreen.js | SettingsScreen.js

Note: All screens have the same structure.

import { View, Text, StyleSheet } from "react-native";

const ProfileScreen = () => {
   return (
      <View style={styles.container}>
         <Text style={styles.text}>Profile Screen</Text>
      </View>
   );
};

export default ProfileScreen;

Tab Navigation Options

Tab Navigator supports options for each tab. These options are passed to the Tab.Screen component.


Example:

Basic Structure:

import { NavigationContainer } from "@react-navigation/native";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import Ionicons from "@expo/vector-icons/Ionicons";

<NavigationContainer>
   <Tab.Navigator
      screenOptions={{
         tabBarLabelPosition: "below-icon",
         tabBarShowLabel: true,
         tabBarActiveTintColor: "Purple",
      }}
   >
      <Tab.Screen
         name="Profile"
         component={ProfileScreen}
         options={{
            tabBarLabel: "My Profile",
            tabBarIcon: ({ color }) => (
               <Ionicons name="person" color={color} size={20} />
            ),
            tabBarBadge: 3,
         }}
      />
   </Tab.Navigator>
</NavigationContainer>;

Tab-Specific Options (Tab.Screen → options)

Option Description
tabBarLabel Label for the tab. Defaults to title or name.
tabBarIcon Function that returns an icon to display in the tab.
tabBarBadge Show a badge on the tab (e.g., number or string).
tabBarBadgeStyle Style object for the badge.
tabBarAccessibilityLabel Accessibility label for the tab.
tabBarAccessibilityHint Accessibility hint for the tab.
tabBarActiveTintColor Color for the active tab icon and label.
tabBarInactiveTintColor Color for the inactive tab icon and label.
tabBarActiveBackgroundColor Background color for the active tab.
tabBarInactiveBackgroundColor Background color for inactive tabs.
tabBarLabelStyle Style object for the tab label text.
tabBarIconStyle Style object for the tab icon.
tabBarItemStyle Style object for the individual tab item.
tabBarStyle Style for the overall tab bar container.
tabBarShowLabel Whether to show the label text below the icon.
tabBarHideOnKeyboard Whether to hide the tab bar when the keyboard is visible (Android only).

You can find more options for Tab.Screen in the official documentation.


Nesting Navigators

Nesting navigators allows us to combine the powers of different types of navigators, creating a seamless and organized user experience.

It’s like having a main road with smaller branching lanes, each having its own set of rules yet interconnected.


Example:

This is a simple example of a nested navigator. We take the Stack Navigator and nest it inside a Tab Navigator.

import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import Home_Screen from "./screens/Home_Screen";
import About_Screen from "./screens/About_Screen";
import { Pressable, Text } from "react-native";

const Stack = createNativeStackNavigator();

export const AboutStack = () => {
   return (
      <Stack.Navigator
         initialRouteName="Home"
         screenOptions={{
            headerStyle: { backgroundColor: "#6a51ae" },
            headerTintColor: "#fff",
            headerTitleStyle: { fontWeight: "bold" },
            headerTitleAlign: "center",
            headerRight: () => (
               <Pressable onPress={() => alert("Menu button pressed!")}>
                  <Text style={{ color: "white", fontSize: 20 }}>Menu</Text>
               </Pressable>
            ),
            contentStyle: { backgroundColor: "#e8e4f3" },
         }}
      >
         <Stack.Screen
            name="Home"
            component={Home_Screen}
            options={{
               title: "Welcome Home!",
            }}
         />
         <Stack.Screen
            name="About"
            component={About_Screen}
            initialParams={{
               name: "Guest",
            }}
            /* options={({ route }) => ({
            title: route.params.name,
         })} */
         />
      </Stack.Navigator>
   );
};

export default function App() {
   return (
      <NavigationContainer>
         <AboutStack />
      </NavigationContainer>
   );
}

Like this:

import { AboutStack } from "./stackApp";

<Tab.Screen
   name="About Stack"
   component={AboutStack}
   options={{
      tabBarLabel: "Home Stack",
      headerShown: false,
      tabBarIcon: ({ color }) => (
         <Ionicons name="home" color={color} size={20} />
      ),
   }}
/>;

About

This Repo is sourced from the YouTube Channel Codevolution

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors