Práctica: Construye una Pantalla de Perfil de Usuario

Video
25 min~10 min lectura
Objetivo de la lección

Esta lección práctica te guiará en la construcción de una Pantalla de Perfil de Usuario completamente funcional.

Puntos de control
  • Introducción: Más Allá del "Hola Mundo"
  • Concepto Clave: El Poder de la Composición de Componentes
  • Cómo Funciona en la Práctica: Anatomía de Nuestra Pantalla
  • Código en Acción: Implementación Completa

Reproductor de video

Introducción: Más Allá del "Hola Mundo"

Has configurado tu entorno, comprendido los componentes básicos de React Native y ahora es el momento de aplicar ese conocimiento en un proyecto tangible. Esta lección práctica te guiará en la construcción de una Pantalla de Perfil de Usuario completamente funcional. Este no es un ejercicio trivial; es un componente fundamental en casi cualquier aplicación moderna, desde redes sociales hasta herramientas de productividad. Aquí consolidarás tu manejo de Views, Text, Image, Stylesheet y ScrollView, mientras introduces conceptos cruciales como el manejo de estados simples, la importación de assets y la creación de interfaces responsivas.

El objetivo final es crear una pantalla que presente información de un usuario de manera atractiva y organizada. Incluiremos una foto de perfil, datos personales, una biografía, una sección de estadísticas y una lista de intereses o habilidades. Aprenderás a estructurar tu código de manera limpia y mantenible, separando la lógica de la presentación y organizando los estilos de forma modular. Esta práctica sienta las bases para componentes más complejos que construirás más adelante en el curso.

Utilizaremos Expo para agilizar el desarrollo, aprovechando su manejo sencillo de imágenes y fuentes. Asegúrate de tener tu servidor de desarrollo de Expo Go en ejecución. Al final de esta lección, no solo tendrás una pantalla lista, sino también la comprensión profunda de cómo se ensamblan las piezas para crear una UI cohesiva en React Native.

Concepto Clave: El Poder de la Composición de Componentes

En React Native, no construyes una pared de una sola pieza; la construyes ladrillo por ladrillo. El concepto de composición es la piedra angular de este framework. En lugar de crear un componente gigante y monolítico que haga todo (un "megacomponente"), diseñas componentes pequeños, enfocados y reutilizables, y luego los combinas para formar interfaces complejas. Piensa en la pantalla de perfil como un escritorio de oficina. No es un mueble macizo, sino un ensamblaje de partes: el cajón para los datos personales (nombre, email), el tablón de anuncios para la biografía, un estante para las estadísticas y varios organizadores pequeños para la lista de intereses.

Cada una de estas partes es un componente independiente. El componente ProfileHeader podría manejar la imagen y el nombre. BioSection se encargaría de la descripción. StatsBar mostraría los números de seguidores y publicaciones. Al componerlos dentro de un componente principal ProfileScreen, logras una estructura clara. Esta modularidad tiene ventajas enormes: facilita las pruebas, permite reutilizar el StatsBar en otras pantallas y hace que el código sea infinitamente más fácil de entender y modificar cuando, por ejemplo, el diseñador quiera cambiar solo la apariencia de la biografía.

Tip del Instructor: Antes de escribir una línea de código, dibuja un esquema de cajas en un papel. Encierra cada sección lógica de tu UI (header, bio, stats) en un rectángulo. Cada uno de esos rectángulos es un candidato a ser un componente separado. Esta práctica, llamada "componentización", es el hábito que separa a los desarrolladores junior de los senior.

Cómo Funciona en la Práctica: Anatomía de Nuestra Pantalla

Vamos a desglosar la construcción paso a paso. Primero, crearemos un nuevo archivo llamado ProfileScreen.js en la carpeta adecuada de nuestro proyecto Expo. Comenzaremos importando los componentes necesarios de React Native y React. Luego, definiremos nuestro componente funcional principal. El primer desafío es la estructura de layout: ¿cómo organizamos los elementos verticalmente y nos aseguramos de que todo sea visible incluso en pantallas pequeñas? Aquí es donde ScrollView se vuelve esencial. Envolveremos todo nuestro contenido en un ScrollView para permitir el desplazamiento.

Dentro del ScrollView, organizaremos los subcomponentes en orden lógico. Empezaremos con un contenedor para la foto de perfil y la información básica, usando View con un estilo de flexDirection: 'row'. Junto a la imagen, colocaremos un View anidado con el nombre, el título profesional y el email, usando flexDirection: 'column'. Debajo, añadiremos la sección de biografía con un Text de múltiples líneas. Luego, crearemos otro contenedor horizontal para las estadísticas, donde cada ítem (por ejemplo, "Publicaciones", "Seguidores", "Siguiendo") será un View hijo con un número y una etiqueta.

Finalmente, renderizaremos la lista de intereses. Para esto, utilizaremos el método map() de JavaScript sobre un array de datos (que inicialmente vivirá en un useState dentro del componente) para generar dinámicamente una serie de View estilizados que actúen como "chips" o etiquetas. Cada paso irá acompañado de la definición de estilos en un objeto StyleSheet.create(), promoviendo buenas prácticas de rendimiento y organización del código.

Código en Acción: Implementación Completa

A continuación, el código completo y funcional para nuestra pantalla de perfil. Nota cómo se organizan las importaciones, la lógica del componente, los datos de estado y los estilos. Copia este código en tu archivo ProfileScreen.js y ejecuta la aplicación en tu emulador o dispositivo.


import React, { useState } from 'react';
import {
  ScrollView,
  View,
  Text,
  Image,
  StyleSheet,
  TouchableOpacity,
} from 'react-native';

const ProfileScreen = () => {
  // Estado para los datos del usuario y sus intereses
  const [userProfile, setUserProfile] = useState({
    name: 'Alex García',
    title: 'Desarrollador Full Stack',
    email: '[email protected]',
    bio: 'Apasionado por la creación de aplicaciones móviles escalables y con una gran experiencia de usuario. Me encanta aprender y compartir conocimientos sobre React Native, Node.js y GraphQL. En mi tiempo libre, practico senderismo y fotografía.',
    stats: {
      posts: 142,
      followers: '2.4k',
      following: 348,
    },
  });

  const [interests, setInterests] = useState([
    'React Native',
    'JavaScript',
    'UI/UX Design',
    'Open Source',
    'Fotografía',
    'Senderismo',
    'Café',
  ]);

  // Función para simular el seguimiento/al usuario
  const handleFollowPress = () => {
    alert(`¡Ahora sigues a ${userProfile.name}!`);
  };

  // Función para simular la edición del perfil
  const handleEditProfilePress = () => {
    alert('Navegaría a la pantalla de edición de perfil.');
  };

  return (
    <ScrollView style={styles.container} showsVerticalScrollIndicator={false}>
      {/* Header con foto e info básica */}
      <View style={styles.header}>
        <Image
          source={{ uri: 'https://randomuser.me/api/portraits/men/32.jpg' }}
          style={styles.profileImage}
        />
        <View style={styles.headerInfo}>
          <Text style={styles.name}>{userProfile.name}</Text>
          <Text style={styles.title}>{userProfile.title}</Text>
          <Text style={styles.email}>{userProfile.email}</Text>
          <View style={styles.headerButtons}>
            <TouchableOpacity style={styles.followButton}
              <Text style={styles.followButtonText}>Seguir</Text>
            </TouchableOpacity>
            <TouchableOpacity style={styles.editButton}
              <Text style={styles.editButtonText}>Editar Perfil</Text>
            </TouchableOpacity>
          </View>
        </View>
      </View>

      {/* Sección de Biografía */}
      <View style={styles.section}>
        <Text style={styles.sectionTitle}>Biografía</Text>
        <Text style={styles.bioText}>{userProfile.bio}</Text>
      </View>

      {/* Sección de Estadísticas */}
      <View style={styles.statsSection}>
        <View style={styles.statItem}>
          <Text style={styles.statNumber}>{userProfile.stats.posts}</Text>
          <Text style={styles.statLabel}>Publicaciones</Text>
        </View>
        <View style={styles.statItem}>
          <Text style={styles.statNumber}>{userProfile.stats.followers}</Text>
          <Text style={styles.statLabel}>Seguidores</Text>
        </View>
        <View style={styles.statItem}>
          <Text style={styles.statNumber}>{userProfile.stats.following}</Text>
          <Text style={styles.statLabel}>Siguiendo</Text>
        </View>
      </View>

      {/* Sección de Intereses */}
      <View style={styles.section}>
        <Text style={styles.sectionTitle}>Intereses</Text>
        <View style={styles.interestsContainer}>
          {interests.map((interest, index) => (
            <View key={index} style={styles.interestChip}>
              <Text style={styles.interestText}>{interest}</Text>
            </View>
          ))}
        </View>
      </View>
    </ScrollView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
    paddingTop: 50,
    paddingHorizontal: 20,
  },
  header: {
    flexDirection: 'row',
    marginBottom: 30,
    alignItems: 'flex-start',
  },
  profileImage: {
    width: 100,
    height: 100,
    borderRadius: 50,
    borderWidth: 3,
    borderColor: '#fff',
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 3, // Para Android
  },
  headerInfo: {
    flex: 1,
    marginLeft: 20,
    justifyContent: 'center',
  },
  name: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#333',
  },
  title: {
    fontSize: 16,
    color: '#666',
    marginTop: 4,
  },
  email: {
    fontSize: 14,
    color: '#888',
    marginTop: 2,
    marginBottom: 12,
  },
  headerButtons: {
    flexDirection: 'row',
  },
  followButton: {
    backgroundColor: '#1e90ff',
    paddingVertical: 8,
    paddingHorizontal: 20,
    borderRadius: 20,
    marginRight: 10,
  },
  followButtonText: {
    color: '#fff',
    fontWeight: '600',
  },
  editButton: {
    backgroundColor: 'transparent',
    paddingVertical: 8,
    paddingHorizontal: 20,
    borderRadius: 20,
    borderWidth: 1,
    borderColor: '#ccc',
  },
  editButtonText: {
    color: '#666',
    fontWeight: '600',
  },
  section: {
    backgroundColor: '#fff',
    borderRadius: 12,
    padding: 20,
    marginBottom: 20,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.05,
    shadowRadius: 3,
    elevation: 2,
  },
  sectionTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#444',
    marginBottom: 12,
  },
  bioText: {
    fontSize: 15,
    lineHeight: 22,
    color: '#555',
  },
  statsSection: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    backgroundColor: '#fff',
    borderRadius: 12,
    paddingVertical: 25,
    marginBottom: 20,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.05,
    shadowRadius: 3,
    elevation: 2,
  },
  statItem: {
    alignItems: 'center',
  },
  statNumber: {
    fontSize: 22,
    fontWeight: 'bold',
    color: '#1e90ff',
  },
  statLabel: {
    fontSize: 13,
    color: '#777',
    marginTop: 5,
  },
  interestsContainer: {
    flexDirection: 'row',
    flexWrap: 'wrap',
  },
  interestChip: {
    backgroundColor: '#eef6ff',
    borderRadius: 16,
    paddingVertical: 8,
    paddingHorizontal: 15,
    marginRight: 10,
    marginBottom: 10,
  },
  interestText: {
    color: '#1e6fcc',
    fontSize: 14,
  },
});

export default ProfileScreen;
    

Para usar esta pantalla, necesitarás importarla y renderizarla en tu componente principal (por ejemplo, en App.js). Asegúrate de tener una conexión a internet para cargar la imagen de perfil desde la URL proporcionada, o reemplázala con una imagen local usando require('./assets/image.jpg').


// App.js
import React from 'react';
import { SafeAreaView, StatusBar } from 'react-native';
import ProfileScreen from './src/screens/ProfileScreen'; // Ajusta la ruta según tu estructura

export default function App() {
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView style={{ flex: 1 }}>
        <ProfileScreen />
      </SafeAreaView>
    </>
  );
}
    

Errores Comunes y Cómo Evitarlos

Al construir esta pantalla, es fácil caer en ciertos patrones problemáticos. Aquí te presento los más frecuentes y su solución:

1. Olvidar el ScrollView en Contenido Largo: Si tu biografía es extensa o tienes muchos intereses, el contenido se desbordará y será inaccesible en pantallas pequeñas. Solución: Siempre envuelve el contenido que pueda exceder la altura de la pantalla en un ScrollView. Usa la prop showsVerticalScrollIndicator={false} para una apariencia más limpia si lo deseas.

2. Estilos en Línea para Todo: Definir estilos directamente en la prop style de cada componente (ej: style={{flex: 1, padding: 10}}) es tentador pero ineficiente. Impacta el rendimiento en re-renderizados y hace el código ilegible. Solución: Utiliza StyleSheet.create() siempre. Centraliza tus estilos, promueve la reutilización y mejora el rendimiento.

3. Keys No Únicas en Listas: En el mapeo de intereses, usar el índice del array como key (como hicimos) es aceptable solo si la lista es estática y nunca se reordena, filtra o modifica. Si la lista es dinámica, es un antipatrón. Solución: Asegúrate de que cada ítem de datos tenga un identificador único y estable (como un id de base de datos) y úsalo como key={`interest-${interest.id}`}.

4. No Considerar el Modo Oscuro: Definir colores fijos como #fff (blanco) para fondos hará que tu pantalla brille con intensidad en un dispositivo con modo oscuro activado. Solución: Para proyectos serios, planea el soporte para modo oscuro desde el inicio. Utiliza el hook useColorScheme de React Native o un sistema de diseño como React Native Paper que lo maneje automáticamente.

5. Imágenes sin Dimensiones Definidas: Si cargas una imagen desde una red y no especificas un ancho y alto, no se renderizará. Solución: Siempre proporciona las propiedades width y height en el estilo de tu componente Image, o usa resizeMode="contain" o "cover" dentro de un contenedor con dimensiones definidas.

Checklist de Dominio

Antes de considerar esta lección completa, verifica que puedes realizar o comprender cada uno de los siguientes puntos:

  • Crear un componente de pantalla funcional desde cero, importando correctamente todos los elementos necesarios de React y React Native.
  • Utilizar el hook useState para gestionar los datos dinámicos del perfil (como la biografía o la lista de intereses).
  • Estructurar una UI compleja utilizando View anidados, manejando correctamente flexDirection (fila y columna) para lograr el layout deseado.
  • Implementar un ScrollView que permita el desplazamiento fluido de todo el contenido cuando excede la pantalla.
  • Estilizar componentes de manera profesional usando StyleSheet.create(), aplicando márgenes, paddings, bordes redondeados, sombras y colores coherentes.
  • Renderizar listas dinámicas de datos (como los intereses) utilizando el método .map(), proporcionando una key única a cada elemento.
  • Incorporar y estilizar componentes interactivos básicos como TouchableOpacity para simular acciones (seguir, editar perfil).
  • Explicar por qué la composición de componentes pequeños es mejor que crear un componente gigante para toda la pantalla.
Falar no WhatsApp
De lección a portfolio

Convertí esta lección en una prueba técnica visible.

Una app pequeña publicada, con README y decisiones explicadas, funciona mejor que una lista de tecnologías sueltas.

Paso 1

Creá una demo mínima que use el concepto de la lección.

Paso 2

Escribí un README corto con objetivo, stack, decisión técnica y mejora futura.

Paso 3

Publicá la demo y enlazala desde tu perfil profesional.

Newsletter Cursalo

Recibí rutas y cursos nuevos

Sumate para recibir recursos orientados a empleo y portfolio.

  • Rutas de empleo
  • Cursos prácticos
  • Portfolio y entrevistas

Sin spam. También podés entrar con tu cuenta para guardar progreso. Iniciá sesión

Práctica: Construye una Pantalla de Perfil de U... | Cursalo