Skip to main content

Build responsive React Native views for any device and support orientation change

Build responsive React Native views for any device and support orientation change

Responsive UI is definitely an important part of web and mobile development. When I started working with React Native more than a year ago, I discovered the hard way that there is no functionality out of the box to support responsiveness.

The goal of this article is to show a complete solution that scales RN UI to all sizes and types of screens (phones, tablets etc) and also supports UI scaling even when the app changes orientation.

How React Native works and what are the problems

React Native style properties accept either percentage or independent pixel (dp) values.

Percentage

Percentage is what we know from “normal” web development. The problem with it, is that not all RN style properties accept it. In example: marginborder-widthborder-radius and many properties do not accept it as value.

That being said, there is no option for a developer to make their app responsive by coding everything in percentage…

Independent pixels

Independent pixels (dp) on the other hand, are not the classic screen pixels (px) that we become accustomed to as web developers. They mathematically connect to screen pixels and the screen’s scale factor through the following equation:

px = dp * scaleFactor

DP can not be used for responsive UI development as someone might think at this point. That is because scaleFactor actually depends on screen’s pixel density, meaning the number of pixels per inch (ppi). What RN can do though, is that it can scale dp values to screen of different sizes only if they have the same number of ppi. But if you think of Android phones — there are thousands out there and most of them have screens with different ppi even if they come from the same manufacturer!

For more info regarding screens and UI factors, you can have a look at Android’s guide for pixel densities hereAndroid’s screen compatibility overview and paintcodeapp’s guide for iPhone resolutions.

Let’s come up with a solution — let’s introduce package react-native-responsive-screen

The idea is really simple! Let’s emulate ourselves the percentage effect, and provide the “correct” dp value for every different screen dynamically. We coded that small and easy solution into a package called react-native-responsive-screen.`

UI responsiveness

In order to create responsive UIs, you need to import and use these 2 methods called widthPercentageToDP and heightPercentageToDP. The names are a bit big but they try to be indicative. That being said, both methods accept a string like percentage ('30%') as an argument and return the percentage of the screen’s actual width or height respectively in dp.

Let’s see this with an example. Samsung A5 2017 Android phone, has a width of 360 dp (this is without taking the scale factor into account); so if we code the following:

<View style={{width: widthPercentageToDP('53%')}} />

it will be translated to:

<View style={{width: 190.8} />

because 53% * 360 dp = (53/100) * 360 dp = 190.8 dp. So if you include these 2 methods within your style process, they will automatically find the correct dp values for every single device. And that happens in a performant way; the package makes sure to calculate screen’s width and height once when the app is initialized and every time the methods are used it simply calls these values to make the calculation instead of identifying them again.

Let’s see a 2nd more detailed example:

import React from 'react';
import { StyleSheet, Text, View, Dimensions } from 'react-native';
import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen';

export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<View style={styles.responsiveBox}>
<Text style={styles.text}>This box is always of 84.5% width and 17% height.</Text>
<Text style={styles.text}>Test it by running this example repo in phones/
emulators with screens of various dimensions and pixel per inch (ppi).</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'gray',
alignItems: 'center',
justifyContent: 'center',
},
responsiveBox: {
width: wp('84.5%'),
height: hp('17%'),
borderWidth: 2,
borderColor: 'orange',
flexDirection: 'column',
justifyContent: 'space-around'
},
text: {
color: 'white'
}
});

What happens here is that we create a simple screen with some text and a view wrapper around it. We code the wrapper’s height to always be the 70% of the screen’s width and the 80% of the screen’s height. Finally we used smaller aliases for the methods’ big names. Check the cross screen result below:

Let’s see a 3rd example of a real production Android app:

Guidelines on how to use these methods

  1. After the package has installed and when the application loads, it detects the screen’s width and height. I.e. for Samsung A5 2017 phone, it detects width: 360DP and height: 640DP (these are the values without taking into account the device's scale factor).
  2. Methods widthPercentageToDP and heightPercentageToDP can be used for any style (CSS) property that accepts DP as value. Properties with DP values are the ones of type number over the props mentioned in RN docs: View style propsText style propsImage style propsLayout props and Shadow props. Use the exposed methods for all of the type numberproperties used in your app in order to make your app fully responsive for all screen sizes.
  3. You can also provide decimal values to these 2 methods, i.e. font-size: widthPercentageToDP('3.75%'). The package methods can be used with or without flex depending on what you want to do and how you choose to implement it.
  4. The suggested approach is to start developing from larger screens (i.e. tablets). That way you are less prone to forget adding responsive values for all properties of type number. In any case, when your screen development is done, you should test it over a big range of different screens as shown below in the How do I know it works for all devices ? section.

Orientation change support

If you want for your application to detect orientation change and adapt the UI itself, you simply need to add an extra listener . This translates into an additional 2 methods: listenOrientationChange that adds the event listener for detecting device orientation change and removeOrientationListener that removes the listener in order not to add new listeners in case the screen is re-mounted (that could lead to performance issues and potential app crashes). To see how to use them, check the example below.

Let’s see how we can add orientation support to our previous example:

import React from 'react';
import { StyleSheet, Text, View, Dimensions } from 'react-native';
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp,
listenOrientationChange as loc,
removeOrientationListener as rol
} from 'react-native-responsive-screen';

export default class App extends React.Component {
componentDidMount() {
loc(this);
}

componentWillUnMount() {
rol();
}
render() {
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'gray',
alignItems: 'center',
justifyContent: 'center',
},
responsiveBox: {
width: wp('84.5%'),
height: hp('17%'),
borderWidth: 2,
borderColor: 'orange',
flexDirection: 'column',
justifyContent: 'space-around'
},
text: {
color: 'white'
}
});

return (
<View style={styles.container}>
<View style={styles.responsiveBox}>
<Text style={styles.text}>This box is always of 84.5% width and 17% height.</Text>
<Text style={styles.text}>Test it by running this example repo in phones/
emulators with screens of various dimensions and pixel per inch (ppi).</Text>
</View>
</View>
);
}
}

What happened now?
1. We added the orientation change listener in componentDidMount lifecycle method.
2. We added the orientation change listener remover in componentWillUnmount lifecycle method. This is a crucial step, because if we don’t do it, a new listener will be registered every time the component re-mounts and can lead to performance issues or even application crash.
3. styles object is created inside the render lifecycle method. That is because every time the listener detects the orientation change it triggers a re-render and thus the styles will be re-created.

And the result looks like that:

The code behind the package

If you want to check the source code of these methods and how they work have a look below. The code is actually pretty small and that’s what triggered me to create a package out of them; a small, easy to use package for responsiveness.

Comments

Popular Posts

How I Reduced the Size of My React Native App by 85%

How and Why You Should Do It I borrowed 25$ from my friend to start a Play Store Developer account to put up my first app. I had already created the app, created the assets and published it in the store. Nobody wants to download a todo list app that costs 25mb of bandwidth and another 25 MB of storage space. So today I am going to share with you how I reduced the size of Tet from 25 MB to around 3.5 MB. Size Matters Like any beginner, I wrote my app using Expo, the awesome React Native platform that makes creating native apps a breeze. There is no native setup, you write javascript and Expo builds the binaries for you. I love everything about Expo except the size of the binaries. Each binary weighs around 25 MB regardless of your app. So the first thing I did was to migrate my existing Expo app to React Native. Migrating to React Native react-native init  a new project with the same name Copy the  source  files over from Expo project Install all de...

How to recover data of your Android KeyStore?

These methods can save you by recovering Key Alias and Key Password and KeyStore Password. This dialog becomes trouble to you? You should always keep the keystore file safe as you will not be able to update your previously uploaded APKs on PlayStore. It always need same keystore file for every version releases. But it’s even worse when you have KeyStore file and you forget any credentials shown in above box. But Good thing is you can recover them with certain tricks [Yes, there are always ways]. So let’s get straight to those ways. 1. Check your log files → For  windows  users, Go to windows file explorer C://Users/your PC name/.AndroidStudio1.4 ( your android studio version )\system\log\idea.log.1 ( or any old log number ) Open your log file in Notepad++ or Any text editor, and search for: android.injected.signing and if you are lucky enough then you will start seeing these. Pandroid.injected.signing.store.file = This is  file path where t...

React Native - Text Input

In this chapter, we will show you how to work with  TextInput  elements in React Native. The Home component will import and render inputs. App.js import React from 'react' ; import Inputs from './inputs.js' const App = () => { return ( < Inputs /> ) } export default App Inputs We will define the initial state. After defining the initial state, we will create the  handleEmail  and the  handlePassword  functions. These functions are used for updating state. The  login()  function will just alert the current value of the state. We will also add some other properties to text inputs to disable auto capitalisation, remove the bottom border on Android devices and set a placeholder. inputs.js import React , { Component } from 'react' import { View , Text , TouchableOpacity , TextInput , StyleSheet } from 'react-native' class Inputs extends Component { state = { ...