Usando SafeAreaView, Estilos con StyleSheet y Pressable en React Native

En este tutorial, aprenderemos cómo organizar la estructura de una aplicación en React Native usando SafeAreaView como contenedor principal, crear pantallas o “screens” dentro de una carpeta src, y cómo gestionar los estilos utilizando StyleSheet. Además, exploraremos cómo usar el componente Pressable en lugar del antiguo Button, que ha sido deprecado.

1. Usando SafeAreaView como Contenedor

Para que la interfaz de usuario se vea bien en dispositivos con diferentes tamaños de pantalla y márgenes, React Native nos ofrece el componente SafeAreaView. Este componente garantiza que los elementos que agregamos dentro de él no se superpongan con áreas no interactivas como la barra de estado o la barra de navegación.

El código básico se vería así:

import React from 'react';
import { SafeAreaView } from 'react-native';
import { HelloWorldScreen } from './src/presentation/screens/HelloWorldScreen';

const App = () => {
  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: '#000' }}>
      <HelloWorldScreen />
    </SafeAreaView>
  );
};

export default App;

Aquí, SafeAreaView actúa como el contenedor principal, y dentro de él colocamos el componente HelloWorldScreen. Establecemos flex: 1 para que ocupe todo el espacio disponible, y un backgroundColor de #000 (negro) para darle un fondo oscuro a nuestra pantalla.

2. Estructura de Archivos: Carpetas y Pantallas

Vamos a organizar el proyecto para que sea más escalable y mantenible. Crearemos una carpeta src y dentro de ella una subcarpeta presentation que contendrá nuestras pantallas (screens). La pantalla HelloWorldScreen tendrá el siguiente contenido:

// src/presentation/screens/HelloWorldScreen.tsx
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

interface Props {
  name?: string;
}

export const HelloWorldScreen = ({ name = 'World' }: Props) => {
  return (
    <View style={styles.container}>
      <Text style={styles.title} numberOfLines={1} ellipsizeMode="tail">
        Hola, {name}
      </Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: 'purple',
  },
  title: {
    fontSize: 45,
    textAlign: 'center',
    color: 'black',
    padding: 20,
  },
});

3. Estilos con StyleSheet.create

En React Native, los estilos se definen utilizando StyleSheet.create, que optimiza el rendimiento al evitar recalcular los estilos en cada renderizado. Al utilizar StyleSheet.create, estamos creando un objeto de estilos que React Native puede manejar de forma más eficiente.

¿Qué hace cada parte del código?

  1. container: Es el estilo que se aplica al componente View. A través de flex: 1, estamos diciendo que este componente debe ocupar todo el espacio disponible. También le asignamos un color de fondo purple y centramos su contenido con justifyContent: 'center'.

  2. title: Este estilo se aplica al componente Text. Aquí definimos:

    • fontSize: 45: Establece el tamaño del texto.
    • textAlign: 'center': Centra el texto horizontalmente.
    • color: 'black': Establece el color del texto a negro.
    • padding: 20: Añade espacio alrededor del texto.

4. Uso de Propiedades en el Componente Text

En el componente Text, podemos ver que usamos las propiedades numberOfLines y ellipsizeMode:

  • numberOfLines={1}: Limita el número de líneas del texto a una sola. Si el texto es más largo de lo que puede caber, se cortará.
  • ellipsizeMode="tail": Cuando el texto se corta, agrega puntos suspensivos (”…”) al final para indicar que el texto ha sido truncado.

5. ¿Por qué Definir el color en .title y No en el container?

El color de nuestro texto se establece en el estilo .title en lugar de en el estilo container porque queremos que solo el texto tenga un color específico. El container define el estilo del contenedor (fondo, flex, alineación, etc.), pero no debe influir en el color del contenido. Esto sigue el principio de separación de responsabilidades, donde cada estilo debe afectar únicamente lo que corresponde a ese componente.

6. Cambiar Estilos Según la Plataforma

Podemos ajustar los estilos en función de la plataforma en la que se esté ejecutando la aplicación. Para hacerlo, usamos Platform.OS. A continuación, te mostramos cómo cambiar el color del texto dependiendo de si estamos en Android o en iOS.

import React from 'react';
import { StyleSheet, Text, View, Platform } from 'react-native';

interface Props {
  name?: string;
}

export const HelloWorldScreen = ({ name = 'World' }: Props) => {
  return (
    <View style={styles.container}>
      <Text style={[styles.title, { color: Platform.OS === 'android' ? 'green' : 'blue' }]}>
        Hola, {name}
      </Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: 'purple',
  },
  title: {
    fontSize: 45,
    textAlign: 'center',
    padding: 20,
  },
});

Aquí estamos usando un condicional dentro de los estilos: si la plataforma es Android, el color del texto será verde; si es iOS, el color será azul.

7. Usando Pressable en Lugar de Button

El componente Button en React Native ha sido deprecado, por lo que ahora se recomienda usar Pressable para manejar interacciones táctiles. A continuación, te mostramos cómo usarlo para implementar un contador que cambie su valor al presionar el botón.

import React, { useState } from 'react';
import { Pressable, Text, View, StyleSheet } from 'react-native';

export const CounterButton = () => {
  const [count, setCount] = useState(0);

  const handlePress = () => {
    setCount(count + 1);
  };

  const handleLongPress = () => {
    setCount(0);
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Contador: {count}</Text>
      <Pressable
        onPress={handlePress}
        onLongPress={handleLongPress}
        style={({ pressed }) => [
          styles.button,
          { backgroundColor: pressed ? 'red' : 'green' },
        ]}
      >
        <Text style={styles.buttonText}>Presiona aquí</Text>
      </Pressable>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'purple',
  },
  title: {
    fontSize: 30,
    color: 'white',
    marginBottom: 20,
  },
  button: {
    padding: 10,
    borderRadius: 5,
  },
  buttonText: {
    color: 'white',
    fontSize: 20,
  },
});

Explicación de Pressable

  • onPress: Se activa cuando el usuario hace un click normal sobre el botón.
  • onLongPress: Se activa cuando el usuario mantiene presionado el botón durante más tiempo.
  • style={({ pressed }) => [...]}: Esta es una función que recibe un objeto pressed, el cual nos indica si el botón está siendo presionado o no. Usamos este valor para cambiar el color de fondo del botón. Si pressed es true, el fondo será rojo, de lo contrario, será verde.

Podes ver mas componentes y sus propidades en la documentacion oficial de react native