Using the Platform Module in React Native

Using the Platform Module in React Native

What is the Platform Module?

React Native’s Platform module is a module present in React Native. It offers a straightforward method for creating platform-specific code that can only be run on particular operating systems. With the help of this module, we may create code that is exclusive to iOS or Android, or we can create code that is cross-platform but has platform-specific features. The great importance of the platform module is that it promotes reusable codes as much as possible, which is the hallmark of react-native. With this module, we don’t have to worry about how to implement different visuals for our Android or iOS applications.

Pre-Requisites

  • Basic understanding of ReactJs

  • Basic understanding of React Native

  • Expo Go installed on our devices (iOS and Android)

  • Basic understanding of CSS

Uses of Platform Module

Below are the reasons why and when we would likely use this module.

  1. Apply different styles to components depending on the platform.

  2. Conditionally render components depending on the platform.

  3. Execute different pieces of code depending on the platform.

  4. Apply fonts or different font sizes depending on the platform.

  5. Use of icons for platform-specific cases.

  6. Request for permissions to use APIs depending on the platform we want.

  7. Platform-specific animations.

  8. Customization f keyboard functions and appearances.

  9. Date and time formatting.

  10. Push notification handling and display.

There are just some use cases of the Platform module.

Installing React Native

npx create-expo-app <name_of_app>

Here is the link to React Native website.

NOTE: we should have Node version 16.13.0 || >=18.0.0

Installing the latest Expo CLI

npm install -g expo-cli

This will install the latest Expo CLI.

Running the app

We have to start our application by running the command below:

npm start

Styling using the Platform Module

Just as stated before, we can use the Platform module to style components according to our choice and platform. Replace the content of App.js with the following:

// path: ./App.js
import { StatusBar } from "expo-status-bar";
import { Platform, StyleSheet, Text, View } from "react-native";

export default function App() {
  return (
    <View style={styles.container}>
      {Platform.OS === "ios" ? (
        <Text style={styles.text}>
          Styling Specific Platforms using the Platform Module (For iOS).
        </Text>
      ) : (
        <Text style={styles.text}>
          Styling Specific Platforms using the Platform Module (For android)
        </Text>
      )}
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: Platform.OS === "ios" ? "grey" : "white",
    alignItems: "center",
    justifyContent: Platform.select({
      ios: "center",
      android: "flex-end",
    }),
    padding: 20,
    textAlign: "center",
  },
  text: {
    color: Platform.OS === "android" ? "black" : "white",
    fontSize: 36,
  },
});

In the code above, in the third line, we import the Platform module. We then used the Platform.OS property to specify what should be displayed as texts on the screen, together with their corresponding styles. And lastly, we also used Platform.OS together with Platform.select in our stylesheet.

Summarily, we specify that for platform ios, we want the backgroundColor to be grey, the content to flex to the center, aligned to the center of the screen and the text should be black. And as for the android platform, we want the text to be aligned to the center but justified to the end of the screen.

Using the Platform Module to select for Navigations

Install the react navigation.

npm install @react-navigation/native

Install dependencies required for our expo managed project.

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

Installing Drawer Navigation

Now, we want to specify the type of navigation we want for the Android and iOS platforms. For Android, we want drawer navigation, and for iOS, we want bottom tab navigation.

This of course can only be possible using the Platform module. So, let us get started.

For the drawer navigation, we need to install @react-navigation/drawer.

npm install @react-navigation/drawer

And because we are running our project on expo, we install the following dependencies:

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

Checkout react-native-gesture-handler and react-native-reanimated for more.

NOTE: add this plugins: ['react-native-reanimated/plugin'], to the babel.config.js file

Installing Bottom-tab Navigation

We have to install the package below:

npm install @react-navigation/bottom-tabs

You can find out more about @react-navigation/bottom-tabs.

This is the time to create our screens. Create a folder called screens. Inside it, first create the file Home.js and add the following piece of code.

// path: ./screens/Home.js
import { StyleSheet, Text, View } from 'react-native';

export default function Home() {
  return (
    <View style={styles.container}>
      <Text>This is the Home Screen</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Creating the Screens

After this, create another file called About.js and add the same content as Home.js, the only difference is the text content.

// path: ./screens/About.js
import { StyleSheet, Text, View } from 'react-native';

export default function About() {
  return (
    <View style={styles.container}>
      <Text>This is the About Screen</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

And finally, for screens, create the screen file History.js and add the following content.

// path: ./screens/History.js
import { StyleSheet, Text, View } from 'react-native';

export default function History() {
  return (
    <View style={styles.container}>
      <Text>This is the History Screen</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Please understand that the Home.js, About.js and History.js are basically to show a text description of the screens along with their stylesheets.

Creating the Navigations

For the navigations, we need the folder navigations. Now we go ahead and create this folder. Inside it , we need to create two files, BottomTabNavigation.js for the iOS display and DrawerNavigation.js for the android display.

The BottomabNavigation.js is as follows:

// path: ./navigations/BottomabNavigation.j
import { NavigationContainer } from "@react-navigation/native";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import Home from "../screens/Home";
import About from "../screens/About";
import History from "../screens/History";

const BottomTab = createBottomTabNavigator();

export default function BottomTabNavigation() {
  return (
    <NavigationContainer>
      <BottomTab.Navigator
        useLegacyImplementation={true}
        initialRouteName="Home"
      >
        <BottomTab.Screen name="Home" component={Home} />
        <BottomTab.Screen name="About" component={About} />
        <BottomTab.Screen name="History" component={History} />
      </BottomTab.Navigator>
    </NavigationContainer>
  );
}

The code above imports the NavigationContainer, which is the container for our bottom tab navigation. It also imports the createBottomTabNavigator() function, which creates bottom tab navigation for us. We also import the screens we have created previously. We then specify the name of the screens and their corresponding component screens.

Next, we have to add content to DrawerNavigation.js. Add the following content.

// path: ./navigations/DrawerNavigation.js
import { NavigationContainer } from "@react-navigation/native";
import { createDrawerNavigator } from '@react-navigation/drawer';

import Home from '../screens/Home';
import About from '../screens/About';
import History from '../screens/History';

const Drawer = createDrawerNavigator();

export default function DrawerNavigation() {
    return (
        <NavigationContainer>
            <Drawer.Navigator useLegacyImplementation={true} initialRouteName="Home">
                <Drawer.Screen name="Home" component={Home} />
                <Drawer.Screen name="About" component={About} />
                <Drawer.Screen name="History" component={History} />
            </Drawer.Navigator>
        </NavigationContainer>
    )
}

This is similar to what we did at the BottomTabNavigation.js. The only difference is that this time we have to make use of the createBottomTabNavigator() function.

Here comes the power of the Platform module. In the App.js of our file, add the following:

// path: ./App.js
import 'react-native-gesture-handler';
import { Platform } from "react-native"
import DrawerNavigation from "./navigations/DrawerNavigation";
import BottomTabNavigation from "./navigations/BottomTabNavigation";

const App = Platform.select({
  ios: () => <BottomTabNavigation />,
  android: () => <DrawerNavigation />
})
export default App;

In the code above we use the platform module to select which navigation to display. Here is the demo of what our platforms will look like.

Android display

iOS display

Using the Platform Module for Native Components that are not common to iOS and Android.

Now that we have looked at how to style for different platforms and select navigations for different platforms using the Platform module. Now we let us use it for Native components that are not common to iOS and Android.

DateTimePicker

First, we start off with the DateTimePicker. We have to use the Platform module because the displays for the platforms android and iOS are different. For instance, the one for android does not show up when the date or time has been selected. For this reason and more, we have to use the Platform module.

Inside our App.js, we have to replace the content with the following.

// path: ./App.js
import { useState } from "react";
import {
  Platform,
  StyleSheet,
  Text,
  View,
  TouchableOpacity,
} from "react-native";
import DateTimePicker from "@react-native-community/datetimepicker";

export default function App() {
  const [date, setDate] = useState(new Date());
  const [showPicker, setShowPicker] = useState(false);

  const handlePickerChange = (event, selectedDate) => {
    setDate(selectedDate || date);
    hideDateTimePicker();
  };

  const showDateTimePicker = () => {
    setShowPicker(true);
  };

  const hideDateTimePicker = () => {
    setShowPicker(false);
  };

  return (
    <View style={styles.container}>
      {Platform.OS === "ios" ? (
        <DateTimePicker
          testID="dateTimePicker"
          value={date}
          mode="datetime" // for iOS only
          display="default"
          onChange={handlePickerChange}
        />
      ) : (
        <>
          {showPicker && (
            <DateTimePicker
              testID="dateTimePicker"
              value={date}
              mode="date" // for android only
              display="default"
              onChange={handlePickerChange}
            />
          )}
          <TouchableOpacity
            style={styles.androidPicker}
            onPress={showDateTimePicker}
          >
            <Text>{date.toLocaleString()}</Text>
          </TouchableOpacity>
        </>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
  androidPicker: {
    backgroundColor: "#f1f1f1",
    padding: 10,
    borderRadius: 5,
  },
});

From the code above, from line 1-10, we import the useState, Platform, and DateTimePicker modules. With the useState, we set states date and showPicker in lines 13 and 14.

In lines 16-27, we create the function handlePickerChange which is responsible for setting the date state and hiding the DateTimePicker module. We also create the showDateTimePicker(), which is responsible for displaying our picker. And lastly, the hideDateTimePicker() function, hides the picker.

Looking at lines 30-56, in line 32, we specified the platform and the DateTimePicker to show. The first ternary value is the display for iOS. And we specify that the mode should be datetime, which is only specific to iOS. And for the Android display, we made sure that it should display when the showPicker value is true. The reason is just as we stated earlier: the DateTimePicker for Android disappears after the date is set. And lastly, for the Android platform, we added the function showDateTimePicker() to TouchableOpacity module to show the DateTimePicker when it is clicked. We also used it to display the value of the date since it does not display by default on android.

And lastly, in lines 62-74, we create our styles.

See the demo below. On the left is the Android platform, and on the right is the iOS platform.

DrawerLayoutAndroid

The DrawerLayoutAndroid does not have an iOS alternative, for this, we will have to install react-native-drawer-layout.Check out react-native-drawer-layout for more information.

To install this run the command below:

npm install react-native-drawer-layout

After that, we will need to install the following:

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

Finally, we add the react-native-reanimated plugin to our babel.config.js file.

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

And we add import react-native-gesture-handler at our entry file, which is the App.js.

Here is our code:

// path: ./App.js
import "react-native-gesture-handler";
import React, { useRef, useState } from "react";
import {
  Button,
  DrawerLayoutAndroid,
  Text,
  StyleSheet,
  View,
  Platform,
} from "react-native";
import { Drawer } from "react-native-drawer-layout";

const App = () => {
  const [open, setOpen] = React.useState(false);

  // create navigation view for android
  const navigationView = () => (
    <View style={styles.container}>
      <Text>I'm the Drawer!</Text>
    </View>
  );

  return (
    <>
      {/* Select for os */}
      {Platform.OS === "ios" ? (
        <Drawer
          open={open}
          onOpen={() => setOpen(true)}
          onClose={() => setOpen(false)}
          renderDrawerContent={() => {
            return (
              <View style={styles.container}>
                <Text>I'm the Drawer!</Text>
              </View>
            );
          }}
        >
          <View style={styles.container}>
            <Text>Swipe from the left (Drawerlayout for iOS)</Text>
          </View>
        </Drawer>
      ) : (
        <DrawerLayoutAndroid
          drawerWidth={300}
          renderNavigationView={navigationView}
        >
          <View style={styles.container}>
            <Text style={styles.paragraph}>
              Swipe from the left (Drawerlayout for Android)
            </Text>
          </View>
        </DrawerLayoutAndroid>
      )}
    </>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center",
    padding: 16,
  },
  paragraph: {
    padding: 16,
    fontSize: 15,
    textAlign: "center",
  },
});

export default App;
  • lines 2-12: we import some modules, among which are the Drawer, DrawerLayoutAndroid, Platform modules and the useState hook.

  • line 15: we create a state variable open to when our drawer should close or open for iOS platform.

  • lines 18-22: the navigation for Android view is created. This will be displayed when the drawer opens.

  • line 27: we used the Platform module to specify for the platforms Android and iOS.

  • lines 28-38: the Drawer of iOS is created. We specify the open state variable, the onClose() and onOpen() functions. And as well as the renderDrawerContent() function, which as the name implies renders the view for iOS drawer.

  • lines 45-54: we specify the drawer for Android. We pass the props drawerWidth, which specifies the width of our drawer. renderNavigationView() function which displays the navigation view of the drawer when it opens.

  • lines 60-72: we style the components.

  • lines 74: we export our app.

Below is the demo of the drawers in the Android and iOS platforms. On the left is Android and on the right is iOS.

NOTE: by default they slide from the left.

Switch

One difference when it comes to using Switch for Android and iOS is the ios_backgroundColor property. It is only specific to iOS. For this reason the Platform module is required.

// path: ./App.js
import React, { useState } from 'react';
import { View, Switch, Platform, StyleSheet } from 'react-native';

export default function App() {
  const [isEnabled, setIsEnabled] = useState(false);

  const toggleSwitch = () => {
    setIsEnabled(previousState => !previousState);
  };

  return (
    <View style={styles.container}>
      {Platform.OS === 'ios' ?
        <Switch
          trackColor={{ false: 'orange', true: 'green' }}
          thumbColor={isEnabled ? '#f5dd4b' : '#f4f3f4'}
          ios_backgroundColor="red" // only available for ios and not for android
          onValueChange={toggleSwitch}
          value={isEnabled}
        />
        :
        <View style={styles.container}>
          <Switch
            trackColor={{ false: 'red', true: 'green' }}
            thumbColor={isEnabled ? '#f5dd4b' : '#f4f3f4'}
            onValueChange={toggleSwitch}
            value={isEnabled}
          />
        </View>
      }
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: 'center'
  }
})

In the code above:

  • lines 1-2: we import the useState hook , Switch and Platform modules.

  • line 7: we create a state variable isEnabled which determines if a switch is enabled or not.

  • line 8: toggleSwitch() function is created which will help us toggle the switch back and forth.

  • lines 16-22: we specify the value for Switch display for iOS. The props trackColor is set to orange when it is false and green when it is true, that is disabled and enabled. We also set the thumbColor for when it is enabled and disabled. And the toggleSwitch() function is called when we change the value of the switch. And lastly, we pass the value of the switch. Note that we passed the ios_backgroundColor which is only specific to iOS as red.

  • lines 24-31: we specify the same for the Android display just as we did above. The difference is that we did not specify ios_backgroundColor and we made the track color to red when it is false, this is to make its background look red.

Below is what our code will look in android and iOS.

StatusBar

The StatusBar is responsible for what we see at the top of our screen. For the StatusBar module, we need to specify for Android or iOS when we want to disable or enable networkActivityIndicatorVisible which is specific to iOS. We also need it when we want to specify the value for the StatusBar background color which is specific to Android.

In App.js file replace the code with this:

// path: ./App.js
import React from "react";
import { Platform, StatusBar, View, Text } from "react-native";

export default function App() {
  const isIOS = Platform.OS === "ios";

  return (
    <View style={{ flex: 1 }}>
      {isIOS && (
        <StatusBar
          barStyle="dark-content"
          networkActivityIndicatorVisible={true}
        />
      )}
      {!isIOS && <StatusBar barStyle="light-content" backgroundColor="red" />}
      <View
        style={{
          flex: 1,
          backgroundColor: "#FFFFFF",
          justifyContent: "center",
          alignItems: "center",
        }}
      >
        <Text>This is about Status Bar. Check the top of the screen.</Text>
      </View>
    </View>
  );
}

From the code above, we used the Platform module to specify networkActivityIndicatorVisible to true for the iOS platform and a backgroundColor of red for Android platform.

See the display below:

Activity Indicator

The activity indicator, as the name suggests, displays an indicator to show an ongoing activity.

For the activityIndicator, when specifying for the different platforms, the size property differs. The iOS platform uses large or small, but the Android platform uses numbers. See the code below.

// path: ./App.js
import { StatusBar } from "expo-status-bar";
import {
  StyleSheet,
  Text,
  View,
  ActivityIndicator,
  Platform,
} from "react-native";

export default function App() {
  return (
    <View style={styles.container}>
      <Text>
        iOS Activity Indicator using size as{" "}
        {Platform.select({
          android: "50",
          ios: "large",
        })}{" "}
      </Text>
      <ActivityIndicator
        size={Platform.OS === "android" ? 50 : "large"}
        animating={true}
        color="blue"
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

In the code above, we used the Platform.select to specify the display text for Android and iOS. And with the Platform.OS, we specified the size for Android as 50 and for iOS as "large".

Here is a preview of our activity indicator.

WebView

Because the WebView is deprecated, we need to install the react-native-webview from the react native community. Find our more about react-native-webview It still has the same props as the latter.

One of the reasons to use the Platform module here is that there are some props that are platform specific. For instance, the cacheEnabled is available on iOS. The geoLocationEnabled and scalesPageToFit which are specific to the android platform.

Run the command below to install this package:

npm i react-native-webview

After that is done, replace the content of App.js with the code below.

// path: ./App.js
import { StyleSheet, Platform } from "react-native";
import { WebView } from "react-native-webview";

export default function App() {
  return Platform.OS === "ios" ? (
    <WebView
      source={{ uri: "https://blog.openreplay.com/" }}
      cacheEnabled={true}
    />
  ) : (
    <WebView
      source={{ uri: "https://blog.openreplay.com/" }}
      geolocationEnabled={true}
      scalesPageToFit={true}
      domStorageEnabled={true}
    />
  );
}

In the code above, using the Platform module we specified the cacheEnabled to true for the iOS platform. And then geoLocationEnabled and scalesPageToFit for the Android platform and true.

Here is the preview below:

Conclusion

In this article, we have looked at the Platform module and why it is useful. We also looked at how to use it on different components and modules that are not specific to iOS and Android.