27. Uso de dimensiones relativas
Tutorial: Uso de Dimensiones Relativas en React Native
📱 Introducción a las Dimensiones Relativas
Las dimensiones relativas son valores que se calculan en función del tamaño de la pantalla del dispositivo, permitiendo que nuestros componentes se adapten automáticamente a diferentes resoluciones y densidades de píxeles.
Problema: Los valores absolutos en píxeles (ej: width: 100, fontSize: 20) se ven diferentes en dispositivos con distintas densidades de pantalla.
Solución: Usar unidades relativas que se escalan según el tamaño de la pantalla.
🎯 Objetivo del Tutorial
Aprender a crear un sistema de dimensiones relativas para:
Evitar código repetitivo con múltiples if-else
Homologar tamaños en diferentes dispositivos
Mantener un código más limpio y mantenible
🔧 Paso 1: El Problema del Código Repetitivo
Código inicial (ineficiente):
tsx
import React from 'react';
import { View, Text, Image, Dimensions, StyleSheet } from 'react-native';
const { width: SCREEN_WIDTH } = Dimensions.get('window');
export const Profile = () => {
// ❌ Múltiples funciones con lógica repetitiva
const getFontSize = () => {
if (SCREEN_WIDTH < 350) return 12;
if (SCREEN_WIDTH < 400) return 14;
if (SCREEN_WIDTH < 450) return 18;
return 20;
};
const getPictureContainerSize = () => {
// Lógica similar para contenedor de imagen
return 100;
};
const getPictureSize = () => {
// Lógica similar para imagen
return 75;
};
// ❌ Llamadas repetidas a las funciones
const pictureContainerSize = getPictureContainerSize();
const pictureSize = getPictureSize();
return (
<View style={styles.container}>
<Text style={[styles.title, { fontSize: getFontSize() }]}>
PERFIL
</Text>
<View style={[
styles.imageContainer,
{
width: pictureContainerSize,
height: pictureContainerSize
}
]}>
<Image
source={{ uri: 'https://ejemplo.com/imagen.jpg' }}
style={[
styles.image,
{ width: pictureSize, height: pictureSize }
]}
/>
</View>
</View>
);
};
Problemas:
✅ Código repetitivo para cada dimensión
✅ Difícil de mantener
✅ Ineficiente (cálculos en cada render)
🚀 Paso 2: Crear una Función de Escala Universal
1. Crear el archivo de utilidades:
text
src/
├── utils/
│ └── scaleUtils.ts
└── screens/
└── Profile.tsx
2. Implementar scaleUtils.ts:
typescript
// src/utils/scaleUtils.ts
import { Dimensions } from 'react-native';
// Obtener dimensiones de la pantalla
const { width: SCREEN_WIDTH } = Dimensions.get('window');
// Ancho base de referencia (ej: iPhone 6/7/8)
const BASE_WIDTH = 380;
// Factor de escala (similar a "rem" en CSS)
const SCALE_FACTOR = SCREEN_WIDTH / BASE_WIDTH;
/**
* Convierte píxeles a unidades escaladas relativas
* @param pixels - Valor en píxeles para el ancho base
* @returns Valor escalado según el tamaño de pantalla
*/
export const getScale = (pixels: number): number => {
return pixels * SCALE_FACTOR;
};
/**
* Escala vertical (para alturas)
* @param pixels - Valor en píxeles
* @returns Valor escalado verticalmente
*/
export const getVerticalScale = (pixels: number): number => {
const { height: SCREEN_HEIGHT } = Dimensions.get('window');
const BASE_HEIGHT = 800; // Altura base de referencia
return pixels * (SCREEN_HEIGHT / BASE_HEIGHT);
};
/**
* Escala moderada con factor de reducción
* @param pixels - Valor en píxeles
* @param factor - Factor de reducción (0-1, default 0.5)
* @returns Valor escalado moderadamente
*/
export const getModerateScale = (
pixels: number,
factor: number = 0.5
): number => {
return pixels + (getScale(pixels) - pixels) * factor;
};
// Exportar dimensiones para uso directo
export const screenWidth = SCREEN_WIDTH;
export const screenHeight = Dimensions.get('window').height;
💡 Paso 3: Aplicar Dimensiones Relativas en Componentes
Profile.tsx optimizado:
tsx
import React from 'react';
import { View, Text, Image, StyleSheet } from 'react-native';
import { getScale, getVerticalScale } from '../utils/scaleUtils';
export const Profile = () => {
// ✅ Usar funciones de escala una vez
const pictureContainerSize = getScale(100); // Relativo al ancho
const pictureSize = getScale(75); // Relativo al ancho
const titleFontSize = getScale(20); // Fuente escalada
return (
<View style={styles.container}>
{/* Título con fuente escalada */}
<Text style={[styles.title, { fontSize: titleFontSize }]}>
PERFIL
</Text>
{/* Contenedor de imagen escalado */}
<View style={[
styles.imageContainer,
{
width: pictureContainerSize,
height: pictureContainerSize
}
]}>
{/* Imagen escalada */}
<Image
source={{ uri: 'https://ejemplo.com/imagen.jpg' }}
style={[
styles.image,
{
width: pictureSize,
height: pictureSize
}
]}
/>
</View>
</View>
);
};
// ✅ Estilos base sin dimensiones absolutas
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
alignItems: 'center',
paddingHorizontal: getScale(16), // Padding relativo
paddingVertical: getVerticalScale(18), // Padding vertical relativo
},
title: {
fontWeight: '900',
textTransform: 'uppercase',
letterSpacing: getScale(4), // Espaciado relativo
marginBottom: getVerticalScale(30), // Margen relativo
},
imageContainer: {
backgroundColor: '#E27683',
borderRadius: getScale(50), // Border radius relativo
justifyContent: 'center',
alignItems: 'center',
marginBottom: getVerticalScale(40),
},
image: {
borderRadius: getScale(37.5), // Border radius relativo
},
});
export default Profile;
📊 Paso 4: Comprender Cómo Funciona el Escalado
Ejemplo de cálculo:
typescript
// Dispositivo 1: Pantalla pequeña (320px de ancho)
const SCREEN_WIDTH_SMALL = 320;
const SCALE_FACTOR_SMALL = 320 / 380 = 0.842;
getScale(100) = 100 * 0.842 = 84.2
// Dispositivo 2: Pantalla grande (414px de ancho)
const SCREEN_WIDTH_LARGE = 414;
const SCALE_FACTOR_LARGE = 414 / 380 = 1.089;
getScale(100) = 100 * 1.089 = 108.9
// Resultado: El componente se escala proporcionalmente
Visualización del efecto:
text
Dispositivo Pequeño (320px) Dispositivo Grande (414px)
┌─────────────────┐ ┌─────────────────────┐
│ │ │ │
│ [84px] │ │ [109px] │
│ │ │ │
└─────────────────┘ └─────────────────────┘
🔄 Paso 5: Escalado con Porcentajes
Alternativa usando porcentajes:
tsx
import React from 'react';
import { View, Text, Image, Dimensions, StyleSheet } from 'react-native';
const { width: SCREEN_WIDTH } = Dimensions.get('window');
export const Profile = () => {
return (
<View style={styles.container}>
{/* Usar porcentaje del ancho de pantalla */}
<View style={[
styles.imageContainer,
{
width: SCREEN_WIDTH * 0.25, // 25% del ancho
height: SCREEN_WIDTH * 0.25 // Mantener relación 1:1
}
]}>
<Image
source={{ uri: 'https://ejemplo.com/imagen.jpg' }}
style={[
styles.image,
{
width: SCREEN_WIDTH * 0.1875, // 18.75% del ancho
height: SCREEN_WIDTH * 0.1875
}
]}
/>
</View>
</View>
);
};
// Calcular porcentajes dinámicamente
export const getPercentage = (percent: number): number => {
return SCREEN_WIDTH * (percent / 100);
};
// Uso: getPercentage(25) = 25% del ancho
🎨 Paso 6: Sistema Completo de Dimensiones Relativas
Archivo completo responsiveUtils.ts:
typescript
// src/utils/responsiveUtils.ts
import { Dimensions, PixelRatio, Platform } from 'react-native';
const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
// Basado en diseño para iPhone 11 (414x896)
const DESIGN_WIDTH = 414;
const DESIGN_HEIGHT = 896;
// Factor de escala horizontal
const scale = (size: number): number =>
(SCREEN_WIDTH / DESIGN_WIDTH) * size;
// Factor de escala vertical
const verticalScale = (size: number): number =>
(SCREEN_HEIGHT / DESIGN_HEIGHT) * size;
// Escala moderada (recomendada para la mayoría de casos)
const moderateScale = (size: number, factor: number = 0.5): number =>
size + (scale(size) - size) * factor;
// Escala basada en densidad de píxeles
const fontScale = (size: number): number => {
const scaleFactor = SCREEN_WIDTH / DESIGN_WIDTH;
const newSize = size * scaleFactor;
if (Platform.OS === 'ios') {
return Math.round(PixelRatio.roundToNearestPixel(newSize));
}
return Math.round(PixelRatio.roundToNearestPixel(newSize)) - 2;
};
// Detectar tipo de dispositivo
const isSmallDevice = SCREEN_WIDTH < 350;
const isMediumDevice = SCREEN_WIDTH >= 350 && SCREEN_WIDTH < 400;
const isLargeDevice = SCREEN_WIDTH >= 400;
const isTablet = SCREEN_WIDTH >= 768;
// Obtener porcentaje del ancho
const widthPercentage = (percent: number): number =>
(SCREEN_WIDTH * percent) / 100;
// Obtener porcentaje del alto
const heightPercentage = (percent: number): number =>
(SCREEN_HEIGHT * percent) / 100;
export default {
scale,
verticalScale,
moderateScale,
fontScale,
isSmallDevice,
isMediumDevice,
isLargeDevice,
isTablet,
widthPercentage,
heightPercentage,
screenWidth: SCREEN_WIDTH,
screenHeight: SCREEN_HEIGHT,
};
📱 Paso 7: Implementación en Proyecto Real
Componente completamente responsivo:
tsx
import React from 'react';
import { View, Text, Image, TextInput, StyleSheet } from 'react-native';
import Responsive from '../utils/responsiveUtils';
const Profile = () => {
return (
<View style={styles.container}>
{/* Título responsivo */}
<Text style={[
styles.title,
{ fontSize: Responsive.fontScale(24) }
]}>
PERFIL
</Text>
{/* Imagen responsiva */}
<View style={[
styles.imageContainer,
{
width: Responsive.scale(100),
height: Responsive.scale(100),
borderRadius: Responsive.scale(50),
}
]}>
<Image
style={[
styles.image,
{
width: Responsive.scale(75),
height: Responsive.scale(75),
borderRadius: Responsive.scale(37.5),
}
]}
source={{ uri: 'https://ejemplo.com/imagen.jpg' }}
/>
</View>
{/* Input responsivo */}
<TextInput
style={[
styles.input,
{
width: Responsive.widthPercentage(90),
height: Responsive.verticalScale(40),
fontSize: Responsive.fontScale(16),
}
]}
placeholder="Nombre"
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
paddingTop: Responsive.verticalScale(40),
},
title: {
fontWeight: '900',
textTransform: 'uppercase',
marginBottom: Responsive.verticalScale(30),
},
imageContainer: {
backgroundColor: '#E27683',
justifyContent: 'center',
alignItems: 'center',
marginBottom: Responsive.verticalScale(40),
},
image: {
// Estilos base
},
input: {
borderWidth: 1,
borderColor: '#ccc',
borderRadius: Responsive.scale(8),
paddingHorizontal: Responsive.scale(12),
},
});
export default Profile;
📊 Paso 8: Comparación de Resultados
Antes (píxeles absolutos):
tsx
// ❌ Se ve diferente en cada dispositivo
<View style={{ width: 100, height: 100 }}>
<Text style={{ fontSize: 20 }}>
Después (dimensiones relativas):
tsx
// ✅ Se escala proporcionalmente
<View style={{
width: getScale(100),
height: getScale(100)
}}>
<Text style={{ fontSize: getScale(20) }}>
Resultados en diferentes dispositivos:
💡 Consejos y Mejores Prácticas
1. Escoger un dispositivo base:
iPhone 11 (414x896) o iPhone 6/7/8 (375x667)
Android común: 360x640
Usar el mismo base para todo el proyecto
2. Cuándo usar cada función:
getScale(): Para anchos, márgenes horizontales, padding horizontal
getVerticalScale(): Para altos, márgenes verticales, padding vertical
getModerateScale(): Para fuentes, iconos, elementos que necesiten escalado más suave
3. Testear en múltiples dispositivos:
typescript
// Agregar logs para debug
console.log(`Dispositivo: ${SCREEN_WIDTH}x${SCREEN_HEIGHT}`);
console.log(`Escala 100px: ${getScale(100)}`);
console.log(`Escala 20px: ${getScale(20)}`);
4. Considerar tablets:
typescript
const isTablet = SCREEN_WIDTH >= 768;
if (isTablet) {
// Layout especial para tablets
return getScale(size) * 0.8; // Reducir escala en tablets
}
🎓 Resumen
Ventajas de usar dimensiones relativas:
✅ Código más limpio: Elimina múltiples if-else
✅ Mantenible: Cambios en un solo lugar afectan toda la app
✅ Consistente: Misma apariencia en diferentes dispositivos
✅ Eficiente: Cálculos optimizados y reutilizables
✅ Escalable: Fácil agregar soporte para nuevos dispositivos
Flujo recomendado:
Crear archivo de utilidades (scaleUtils.ts)
Definir ancho base de referencia
Usar getScale() en lugar de valores absolutos
Testear en al menos 3 tamaños de pantalla diferentes
Ajustar factor base según necesidades del diseño
📚 Recursos Adicionales
Librerías populares:
react-native-size-matters: Sistema completo de escalado
react-native-responsive-dimensions: Porcentajes y escalado
react-native-responsive-screen: Métodos de pantalla responsiva
Lecturas recomendadas:
🚀 Conclusión
Las dimensiones relativas son esenciales para crear aplicaciones React Native que funcionen bien en la amplia variedad de dispositivos Android e iOS. Al implementar un sistema de escalado como getScale(), aseguras que tu aplicación se vea profesional y consistente, independientemente del dispositivo del usuario.
¡Recuerda: Una vez configurado tu sistema de dimensiones relativas, úsalo consistentemente en todo tu proyecto para obtener los mejores resultados
Comentarios
Publicar un comentario