DEV Community

loading...
Cover image for React Native: Adaptive layout for UI

React Native: Adaptive layout for UI

TawalMc
Power and Process engineer student and software dev. Learning software development with JavaScript/NodeJS, React/React Native, C++, Kotlin.
・4 min read

Improve UI/UX by adapting layout for screen orientation.
You can get the code of this project on my Github: 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
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
    for landscape.

    How to structure UI layout ?

    To answer this question, see how we cut our portrait ui in the following picture
    Alt Text
    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

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,
  },
});
Enter fullscreen mode Exit fullscreen mode

The most important part of this code are

const windowWidth = useWindowDimensions().width;
const windowHeight = useWindowDimensions().height;
Enter fullscreen mode Exit fullscreen mode

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

flexDirection: windowWidth < windowHeight ? 'column' : 'row'
Enter fullscreen mode Exit fullscreen mode
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

of Rahul. Bye!

Discussion (2)

Collapse
said34 profile image
Said34

Great

Collapse
tawalmc profile image
TawalMc Author

Thank you guy