React Native: Adaptive layout for UI
Tags: react, reactnative, responsive, ui
Published: 2021-02-17 20:14:44

Improve UI/UX by adapting layout for screen orientation.
You can get the code of this project on my Github: [rn_adaptive_ui](https://github.com/TawalMc/blog_codes/tree/master/rn_codes/rn_adaptive_ui). 

Hi everyone, I hope you're doing well. I will share with you a little solution that I have found when creating React Native app and want to adapt your layout to the user screen orientation. On this topic there are many solutions like some packages to listen the user orientation change  or use simply scrollview component to scroll on screen when components or data overflow. But here I will talk about how to structure your layout and how to use the **useWindowDimensions** hook of react native to adapt your layout. And to do this, I will take an example of adaptive UI to reproduce.
#The problem
Here is the UI
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u6aklkkngjsvwm8mu4vr.png)
for portrait. But the problem is, what to get if it is the landscape orientation.
To answer this, if you have a designer, great but if you don't have it is not a problem.
Just ask some questions:

- What are the important parts of the UI that user must easily see or interact with them

- In how many parts will we divide our UI to make it adaptive ?
So to avoid you many reflections, just keep this
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dh8jmnau6i9trf478m6a.png)
for landscape.
# **How to structure UI layout ?** 
To answer this question, see how we cut our portrait ui in the following picture
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8402lryd4tqqoijqurwc.png)
In this picture you can see: 

- The **I** box which represent our main layout component
- The **1** box which represent the first child among the two
- The **2** box which represent the second and last child.

So the **1** and **2** box are in the same high order. And with this structure, we can easily adapt our ui by setting the main layout (**I**) from column to row direction. Look at this code
```javascript
import React, { useEffect } from 'react';
import {
  SafeAreaView,
  Text,
  View,
  StyleSheet,
  TouchableOpacity,
  useWindowDimensions,
} from 'react-native';
import Battery from './assets/images/battery.svg';
import Microchip from './assets/images/microchip.svg';
import Speedometer from './assets/images/speedometer.svg';
import Wheel from './assets/images/wheel.svg';

const PhoneStatusEltArray = [
  {
    name: 'Battery',
    status: '5h40mn',
    Icon: Battery,
  },
  {
    name: 'Performance mode',
    status: 'Optimized',
    Icon: Speedometer,
  },
  {
    name: 'Storage',
    status: '11.8 GB free',
    Icon: Wheel,
  },
  {
    name: 'memory',
    status: '1.9 GB free',
    Icon: Microchip,
  },
];

function PhoneStatusElt({ name, Icon, status }) {
  return (
    <View style={styles.phoneStatusEltContainer}>
      <View style={styles.statusEltImg}>
        <Icon width={24} height={24} fill="black" />
      </View>
      <Text
        style={{
          textAlign: 'center',
          fontSize: 12,
          fontWeight: 'bold',
          marginBottom: 5,
        }}
      >
        {name}
      </Text>
      <Text
        style={{
          textAlign: 'center',
          color: 'grey',
          fontSize: 12,
          position: 'absolute',
          bottom: 0,
        }}
      >
        {status}
      </Text>
    </View>
  );
}

export default function App() {
  const windowWidth = useWindowDimensions().width;
  const windowHeight = useWindowDimensions().height;

  useEffect(() => {
    console.log(windowWidth);
  });
  return (
    <SafeAreaView
      style={{
        flexDirection: windowWidth < windowHeight ? 'column' : 'row',
        flex: 1,
        alignItems: 'center',
      }}
    >
      <View style={styles.subContainer}>
        <View style={styles.circleContainer}>
          <View style={styles.percentContainer}>
            <Text style={{ fontSize: 50, color: 'lightblue' }}>99</Text>
            <Text style={{ fontSize: 15 }}>/100</Text>
          </View>
          <Text
            style={{
              color: 'lightblue',
              fontSize: 20,
              textTransform: 'uppercase',
            }}
          >
            Excellent!
          </Text>
        </View>
      </View>
      <View style={styles.subContainer}>
        <Text
          style={{
            textAlign: 'center',
            paddingHorizontal: 25,
            marginBottom: 15,
            fontSize: 17,
          }}
        >
          Your phone's maintenance status is excellent. Tap below to improve it.
          This won't affect your personal data.
        </Text>
        <TouchableOpacity style={styles.buttonOptimize} activeOpacity={0.7}>
          <Text style={{ color: 'white', fontSize: 20 }}>OPTIMISE NOW</Text>
        </TouchableOpacity>
        <View style={styles.statusEltContainer}>
          {PhoneStatusEltArray.map((item) => (
            <PhoneStatusElt
              key={item.name}
              name={item.name}
              Icon={item.Icon}
              status={item.status}
            />
          ))}
        </View>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  subContainer: {
    flex: 1,
    width: '100%',
    alignItems: 'center',
    justifyContent: 'center',
  },
  circleContainer: {
    width: 220,
    height: 220,
    borderRadius: 110,
    borderWidth: 2,
    borderColor: 'lightblue',
    alignItems: 'center',
    justifyContent: 'center',
  },
  percentContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 10,
  },
  buttonOptimize: {
    backgroundColor: 'lightblue',
    paddingHorizontal: 40,
    paddingVertical: 15,
    borderRadius: 40,
    marginVertical: 10,
  },
  phoneStatusEltContainer: {
    alignItems: 'center',
    width: 70,
    height: 100,
    marginHorizontal: 8,
  },
  statusEltImg: {
    padding: 15,
    borderRadius: 20,
    borderWidth: 1,
    borderColor: 'grey',
    marginBottom: 5,
  },
  statusEltContainer: {
    width: '100%',
    paddingVertical: 5,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-around',
    marginTop: 15,
    paddingHorizontal: 5,
  },
});
```
The most important part of this code are
```javascript
const windowWidth = useWindowDimensions().width;
const windowHeight = useWindowDimensions().height;
```
In this code we use **useWindowDimensions** code which get the current **width** and **height** of screen. It means that when the orientation changes, its updates these two values. So we need just to compare these values to know when it is landscapes or portrait and so change the flex direction of the main layout (**I**) . And so we have 
```javascript
flexDirection: windowWidth < windowHeight ? 'column' : 'row'
```
#####NB: When screen orientation is landscape, **width** is superior to **height**.
So... we can update easily our layout by using **useWindowDimensions** hook values.
#Conclusion
With this hook and the structure of layout we can adapt the UI when screen orientation changes. The main problem is to find the landscapes UI and after do a good structure of layout to easily change some styles properties like **flexDirection**, **justifyContent**,... And also I suggest you this article {% link https://dev.to/holdmypotion/react-native-responsive-and-adaptive-user-interfaces-3c6d %} of **Rahul**. Bye!