26. Creación de diseños adaptativos para diferentes tamaños de pantalla

 

Tutorial: Creación de Diseños Adaptativos para Diferentes Tamaños de Pantalla

📱 Introducción

En React Native, crear diseños que se adapten a diferentes tamaños de pantalla es crucial para garantizar una experiencia de usuario consistente en todos los dispositivos. Este tutorial te enseñará técnicas para hacer tus aplicaciones responsivas.


🎯 El Problema: Diferentes Densidades de Pantalla

Escenario común:

  • 📱 Dispositivo de gama alta: Pantalla Full HD (1080x1920)

  • 📱 Dispositivo antiguo: Pantalla WVGA (480x800)

Problema: Los mismos píxeles (ej: fontSize: 20) se ven más grandes en pantallas de baja densidad y más pequeños en pantallas de alta densidad.


🛠️ Paso 1: Configurar Múltiples Emuladores

1. Crear emuladores diferentes en Android Studio:

  1. Abre Android Studio

  2. Ve a Tools → AVD Manager (Android Virtual Device Manager)

  3. Crea dispositivos con diferentes resoluciones:

    • Pixel 2: 1080x1920 (alta densidad)

    • Nexus S: 480x800 (baja densidad)

2. Ejecutar en múltiples dispositivos:

bash

# Con ambos emuladores abiertos, ejecuta:

npm run android

# React Native instalará la app en TODOS los dispositivos conectados


📏 Paso 2: Usar Dimensions API para Medir la Pantalla

Obtener dimensiones del dispositivo:

tsx

import { Dimensions } from 'react-native';


// Obtener dimensiones de la pantalla

const screenWidth = Dimensions.get('screen').width;

const screenHeight = Dimensions.get('screen').height;


// Obtener dimensiones de la ventana (excluye barras de estado/notificaciones)

const windowWidth = Dimensions.get('window').width;

const windowHeight = Dimensions.get('window').height;


console.log(`Ancho de pantalla: ${screenWidth}`);

console.log(`Alto de pantalla: ${screenHeight}`);

Ejemplo práctico:

tsx

import React from 'react';

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


const App = () => {

  const screenWidth = Dimensions.get('window').width;

  

  return (

    <View style={styles.container}>

      <Text style={[styles.title, { fontSize: calculateFontSize(screenWidth) }]}>

        Título Responsivo

      </Text>

    </View>

  );

};


const calculateFontSize = (screenWidth: number): number => {

  console.log(`Ancho actual: ${screenWidth}`);

  

  if (screenWidth < 350) {

    console.log('Pantalla muy pequeña - fontSize: 12');

    return 12;

  } else if (screenWidth < 400) {

    console.log('Pantalla pequeña - fontSize: 14');

    return 14;

  } else if (screenWidth < 450) {

    console.log('Pantalla mediana - fontSize: 18');

    return 18;

  } else {

    console.log('Pantalla grande - fontSize: 20');

    return 20;

  }

};


const styles = StyleSheet.create({

  container: {

    flex: 1,

    justifyContent: 'center',

    alignItems: 'center',

  },

  title: {

    fontWeight: 'bold',

  },

});


export default App;


🔧 Paso 3: Implementar Funciones Helper para Responsividad

Versión mejorada con valores escalables:

tsx

// src/utils/responsiveUtils.ts

import { Dimensions } from 'react-native';


const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');


// Tamaño base de referencia (basado en iPhone X)

const BASE_WIDTH = 375;

const BASE_HEIGHT = 812;


/**

 * Escala un valor basado en el ancho de pantalla

 */

export const scale = (size: number): number => {

  return (SCREEN_WIDTH / BASE_WIDTH) * size;

};


/**

 * Escala verticalmente basado en el alto de pantalla

 */

export const verticalScale = (size: number): number => {

  return (SCREEN_HEIGHT / BASE_HEIGHT) * size;

};


/**

 * Escala moderada con factor de reducción

 */

export const moderateScale = (size: number, factor: number = 0.5): number => {

  return size + (scale(size) - size) * factor;

};


/**

 * Obtiene tamaño de fuente responsivo

 */

export const responsiveFontSize = (size: number): number => {

  const scaleFactor = SCREEN_WIDTH / BASE_WIDTH;

  const newSize = size * scaleFactor;

  

  // Limitar tamaño mínimo y máximo

  return Math.max(12, Math.min(newSize, size * 1.5));

};


🎨 Paso 4: Implementar Diseños Adaptativos Completos

Ejemplo de pantalla de perfil responsiva:

tsx

import React from 'react';

import {

  View,

  Text,

  Image,

  TextInput,

  StyleSheet,

  Dimensions,

} from 'react-native';

import {

  scale,

  verticalScale,

  moderateScale,

  responsiveFontSize,

} from './src/utils/responsiveUtils';


const { width: SCREEN_WIDTH } = Dimensions.get('window');


export const Profile = () => {

  // Tamaños dinámicos basados en pantalla

  const containerPadding = moderateScale(16);

  const imageSize = SCREEN_WIDTH < 400 ? scale(80) : scale(100);

  const titleFontSize = responsiveFontSize(24);

  const inputFontSize = responsiveFontSize(16);


  return (

    <View style={[styles.container, { padding: containerPadding }]}>

      {/* Título con fuente responsiva */}

      <Text style={[styles.title, { fontSize: titleFontSize }]}>

        PERFIL

      </Text>


      {/* Imagen con tamaño responsivo */}

      <View style={[styles.imageContainer, { width: imageSize, height: imageSize }]}>

        <Image

          source={{ uri: 'https://ejemplo.com/imagen.jpg' }}

          style={[styles.image, { width: imageSize * 0.75, height: imageSize * 0.75 }]}

        />

      </View>


      {/* Campos de entrada con tamaños adaptativos */}

      <View style={styles.inputGroup}>

        <Text style={[styles.label, { fontSize: responsiveFontSize(10) }]}>

          NOMBRE *

        </Text>

        <TextInput

          style={[styles.input, { fontSize: inputFontSize, height: verticalScale(40) }]}

          placeholder="Ingresa tu nombre"

        />

      </View>


      {/* Más elementos con tamaños responsivos... */}

    </View>

  );

};


const styles = StyleSheet.create({

  container: {

    flex: 1,

    backgroundColor: 'white',

    alignItems: 'center',

  },

  title: {

    fontWeight: '900',

    textTransform: 'uppercase',

    letterSpacing: scale(4),

    marginBottom: verticalScale(30),

    textAlign: 'center',

  },

  imageContainer: {

    backgroundColor: '#E27683',

    borderRadius: scale(50),

    justifyContent: 'center',

    alignItems: 'center',

    marginBottom: verticalScale(40),

  },

  image: {

    borderRadius: scale(37.5),

  },

  inputGroup: {

    width: '100%',

    marginBottom: verticalScale(20),

  },

  label: {

    color: '#099',

    textTransform: 'uppercase',

    fontWeight: 'bold',

    marginBottom: verticalScale(5),

  },

  input: {

    width: '100%',

    borderBottomWidth: 1,

    borderBottomColor: '#000',

  },

});


📱 Paso 5: Usar Porcentajes y Flexbox

Técnicas para layouts responsivos:

1. Usar porcentajes:

tsx

<View style={{ width: '90%' }}>  // 90% del ancho del padre

<View style={{ height: '50%' }}> // 50% del alto del padre

2. Flexbox para distribución:

tsx

<View style={styles.container}>

  <View style={styles.header} />      // Altura fija

  <View style={styles.content} />     // Toma espacio restante

  <View style={styles.footer} />      // Altura fija

</View>


const styles = StyleSheet.create({

  container: {

    flex: 1,

  },

  header: {

    height: 60, // Altura fija para elementos críticos

  },

  content: {

    flex: 1,    // Toma todo el espacio disponible

  },

  footer: {

    height: 50, // Altura fija

  },

});

3. Aspect Ratio:

tsx

<Image

  source={require('./image.jpg')}

  style={{ width: '100%', aspectRatio: 16/9 }} // Mantiene relación 16:9

/>


🔄 Paso 6: Manejar Cambios de Orientación

Detectar cambios de orientación:

tsx

import React, { useState, useEffect } from 'react';

import { Dimensions, View, Text } from 'react-native';


const App = () => {

  const [orientation, setOrientation] = useState('portrait');


  useEffect(() => {

    const updateOrientation = () => {

      const { width, height } = Dimensions.get('window');

      setOrientation(width > height ? 'landscape' : 'portrait');

    };


    // Escuchar cambios de dimensiones

    const subscription = Dimensions.addEventListener('change', updateOrientation);

    

    // Verificar orientación inicial

    updateOrientation();


    // Limpiar suscripción

    return () => subscription?.remove();

  }, []);


  return (

    <View style={orientation === 'portrait' ? styles.portrait : styles.landscape}>

      <Text>Orientación: {orientation}</Text>

    </View>

  );

};


const styles = StyleSheet.create({

  portrait: {

    flex: 1,

    flexDirection: 'column',

  },

  landscape: {

    flex: 1,

    flexDirection: 'row',

  },

});


📊 Paso 7: Crear Breakpoints para Diferentes Dispositivos

Sistema de breakpoints:

tsx

// src/constants/Breakpoints.ts

export const Breakpoints = {

  // Pequeños dispositivos (iPhone SE, etc.)

  SMALL: 375,

  

  // Dispositivos medianos (iPhone 12, etc.)

  MEDIUM: 414,

  

  // Dispositivos grandes (tablets pequeñas)

  LARGE: 768,

  

  // Tablets grandes

  XLARGE: 1024,

};


// Hook personalizado para breakpoints

export const useBreakpoint = () => {

  const { width } = Dimensions.get('window');

  

  return {

    isSmall: width < Breakpoints.SMALL,

    isMedium: width >= Breakpoints.SMALL && width < Breakpoints.MEDIUM,

    isLarge: width >= Breakpoints.MEDIUM && width < Breakpoints.LARGE,

    isXLarge: width >= Breakpoints.LARGE,

    currentWidth: width,

  };

};

Uso en componentes:

tsx

import { useBreakpoint } from './src/constants/Breakpoints';


const MyComponent = () => {

  const { isSmall, isMedium, isLarge } = useBreakpoint();

  

  return (

    <View style={[

      styles.container,

      isSmall && styles.smallContainer,

      isMedium && styles.mediumContainer,

      isLarge && styles.largeContainer,

    ]}>

      <Text style={[

        styles.text,

        isSmall && { fontSize: 14 },

        isMedium && { fontSize: 16 },

        isLarge && { fontSize: 18 },

      ]}>

        Texto Responsivo

      </Text>

    </View>

  );

};


🎯 Paso 8: Mejores Prácticas para Diseños Adaptativos

1. Usar valores relativos en lugar de absolutos:

tsx

// ❌ EVITAR (píxeles fijos)

fontSize: 20,

width: 300,

margin: 10,


// ✅ PREFERIR (valores relativos)

fontSize: responsiveFontSize(20),

width: scale(300),

margin: moderateScale(10),

2. Priorizar Flexbox:

tsx

// Container principal siempre flex: 1

<View style={{ flex: 1 }}>

  {/* Elementos que se expanden */}

  <View style={{ flex: 1 }} />

  

  {/* Elementos de tamaño fijo o mínimo */}

  <View style={{ minHeight: 50 }} />

</View>

3. Testear en múltiples dispositivos:

  • 📱 Smartphones pequeños (375px)

  • 📱 Smartphones normales (414px)

  • 📱 Smartphones grandes (480px+)

  • 📱 Tablets (768px+)

4. Considerar accesibilidad:

tsx

// Permitir ajuste de tamaño de texto del sistema

<Text style={styles.text} allowFontScaling={true}>

  Texto accesible

</Text>


📝 Resumen de Técnicas

Técnica

Cuándo Usar

Ejemplo

Dimensions API

Medir pantalla

Dimensions.get('window').width

Funciones de escala

Tamaños responsivos

scale(20), verticalScale(40)

Porcentajes

Elementos que deben ocupar porcentaje fijo

width: '90%'

Flexbox

Distribución flexible de espacio

flex: 1, flexDirection: 'row'

Aspect Ratio

Mantener proporciones

aspectRatio: 16/9

Breakpoints

Diseños específicos por tamaño

isTablet ? styles.tablet : styles.phone


🚀 Conclusión

Crear diseños adaptativos en React Native requiere:

  1. Medir la pantalla con Dimensions

  2. Escalar valores con funciones helper

  3. Usar Flexbox para layouts flexibles

  4. Testear en múltiples dispositivos y orientaciones

  5. Considerar la accesibilidad y preferencias del usuario

Recursos adicionales:

¡Ahora puedes crear aplicaciones que se vean geniales en cualquier dispositivo! 


Comentarios

Entradas más populares de este blog

17. Estructura de una aplicación de React Native, una vista mas profunda

12. Configuracion del entorno de desarrollo en Windows

Guía Paso a Paso para Entender React Native (antes del Tutorial)