SafeAreaView vs useSafeAreaInsets  - ¿Cual usar ?

SafeAreaView vs useSafeAreaInsets - ¿Cual usar ?

Como hacer un StatusBar dinamico para iOS y android usando useSafeAreaInsets

TL:DR
En este post explicaré porque debemos utilizar el hook useSafeAreaInsets en lugar de SafeAreaView y que beneficios tenemos al utilizar useSafeAreaInsets, haciendo un ejemplo implementando un StatusBar componente genérico con diferentes colores de fondo. Código al final del articulo.

SafeAreaView vs useSafeAreaInsets

Cuando trabajamos con React Native, tenemos dos maneras de trabajar con nuestras pantallas para aquellos dispositivos que cuentan pantalla notch o islas dinámicas.

Por un lado tenemos el componente SafeAreaView que nos proporciona react-native y por otro lado tenemos el mismo componente llamado SafeAreaView y el hook useSafeAreaInsets pero ambos provenientes de react-native-safe-area-context . Lo recomendable usar react-native-safe-area-context y si usamos el hook useSafeAreaInsets podremos hacer hacer cosas mas dinamicas con nuestras pantallas para tener una mejor UI.

Un claro ejemplo de las principales ventajas de useSafeAreaInsets es que podemos hacer un componente con un status bar que se adapte a un a un color que necesitemos. Actualmente react native cuenta con el componente StatusBar . Si bien con este componente podemos cambiar los colores de la barra de estado de nuestro en android, en iOS no podemos hacerlo, lo unico que podemos hacer es cambiar los iconos de la barra. Puedes revisar el componente aqui.
Que pantalla visualmente se ve mejor?

Quiero pensar que has dicho la segunda pantalla...

Pongamoslo a prueba 🚀

Bien, supongamos que tu aplicación tiene que cambiar el backgroundColor del StatusBar dependiendo de la situación. Esto con SafeAreaView no podría ser posible y si lo intentamos hacer con StatusBar de react-native solo podríamos cambiar el color en android.

Es por eso que vamos a hacer uso del hook useSafeAreaInsets y con el haremos un componente genérico para poder usar en todas nuestras pantallas implementadas. Lo primero que tenemos que hacer es instalar la dependencia en nuestro proyecto con el gestor de paquetes que estes utilizando, en mi caso usamos yarn

 $ yarn add react-native-safe-area-context

Ahora agregaremos el provider a nuestro archivo App.tsx

import React from 'react';

import {SafeAreaProvider} from 'react-native-safe-area-context';
import {Home} from './src/screens/Home';

function App(): React.JSX.Element {
  return (
    <SafeAreaProvider>
      <Home />
    </SafeAreaProvider>
  );
}

export default App;

No me centrare en los detalles, pero agregue un componente llamado home en mi carpeta src/screens/Home, puedes llamarlo como gustes y recomiendo que lo hagas para que sigas el ejemplo.

Una vez que hemos agregado nuestro provider es hora de poner en marcha nuestra pantalla.

Cuando utilizamos el componente SafeAreaView, nuestra aplicación aplica un contenedor que contiene definido el padding de la pantalla que incluye los valores por default del dispositivo, es decir, si tiene notch en la parte superior, inferior o en los laterales. Este componente tiene por default los 4 edges

<SafeAreaView edges={['right', 'bottom', 'left', 'top']} />

Si queremos decirle a nuestro SafeAreaView que no coloque el padding de la parte superior, solo tendremos que remover el valor top. Probablemente con esto podríamos hacer nuestro StatusBar pero no es suficiente, ya que necesitaremos el valor de paddingTop, es decir el alto del notch, para establecer nuestro componente.

Con el hook useSafeAreaInsets podemos extraer el valor top, bottom, left y right del notch de nuestro dispositivo. Con esto podremos crear nuestro StatusBar con un color que haga que luzca de acuerdo a nuestra pantalla/componente.

export const Home = () => {
  const { top } = useSafeAreaInsets();

  const backgroundStyle = {
    backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
    flex: 1,
  };

  return (
    <View style={styles.container}>
      <View style={backgroundStyle}>
        <HeaderCard />
        <View style={styles.otherContent}>
          <Text style={styles.text}>Other Content</Text>
        </View>
      </View>
    </View>
  );
};

Si observas, aquí extraemos el valor top del hook useSafeAreaInsets, este lo podemos agregar al styles.container, sin embargo al hacer eso, obtendremos el mismo comportamiento que el componente SafeAreaView y es lo que queremos evitar.

Entonces lo que haremos es agregar otro componente al que le pasaremos el valor heigth con el valor de top:

export const Home = () => {
  const {top} = useSafeAreaInsets();
  const isDarkMode = useColorScheme() === 'dark';

  const backgroundStyle = {
    backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
    flex: 1,
  };

  return (
    <View style={styles.container}>
      //Este sera nuestro componente que realizara la funcion del status bar
      <View style={StyleSheet.compose(styles.statusBar, {height: top})} /> 
      <View style={backgroundStyle}>
        <HeaderCard />
        <View style={styles.otherContent}>
          <Text style={styles.text}>Other Content</Text>
        </View>
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  statusBar: {
    backgroundColor: '#622ED0',
  },
  otherContent: {
    flex: 1,
    backgroundColor: 'white',
    alignItems: 'center',
    justifyContent: 'center',
  },
  text: {
    fontSize: 30,
  },
});

Y bingo 🥊 ! Con esto hemos podido obtener un StatusBar con un backgroundColor diferente que permite ser mas dinámico e intuitivo en nuestra UI.

Podemos ponernos mas creativos y hacer un con Contenedor genérico que nos permita mediante el paso de props, cambiar el backgroundColor, barStyle o incluso cualquier otro valor para poder manipular nuestras pantallas que tienen y no tienen notch.

Este ejemplo también aplica para dispositivos que no tienen notch:

Espero este post te haya sido útil y te quede un poco mas claro que en casos como este, useSafeAreaInsets es de mayor valor para nuestra aplicación. Puedes revisar el código de este ejemplo en mi repositorio.