Cámara y galería en React Native

Clase 21 de 23Curso de Fundamentos de React Native

Resumen

Integra la cámara y la galería del sistema operativo para crear un perfil completo con avatar, persistencia local y generación con IA. Aquí verás cómo estructurar el componente de avatar, manejar estados con contexto, solicitar permisos y guardar datos de forma segura en el dispositivo. Todo con un flujo claro y práctico.

¿Cómo se integra la librería de cámara y galería del sistema?

Usa una librería que accede a la cámara y a la galería nativas. Se importa como ImagePicker, se configuran opciones como la calidad (por ejemplo, 0.8) y se manejan los casos de cancelación. Además, se validan permisos de cámara y se muestran alertas cuando no han sido otorgados. El ejemplo se apoya en el esquema del sistema operativo, en este caso iOS, sin soluciones externas.

¿Qué opciones y permisos se configuran?

  • Importar las funciones de ImagePicker en la parte superior.
  • Definir calidad entre 0 y 1: 0.8 recomendado para equilibrio.
  • Comprobar si el usuario canceló: no actualizar el estado.
  • Solicitar y validar permisos de cámara.
  • Mostrar una alerta si no hay permisos.
// importación en la parte superior
import * as ImagePicker from 'image-picker';

// elegir desde galería
const chooseFromGallery = async () => {
  const result = await ImagePicker.launchImageLibraryAsync({
    // tipo de imagen según librería.
    quality: 0.8,
  });
  if (result.canceled) return;
  setAvatar(result.assets?.[0]?.uri);
};

// tomar foto con cámara
const takePhoto = async () => {
  const { status } = await ImagePicker.requestCameraPermissionsAsync();
  if (status !== 'granted') {
    alert('otorga permisos de cámara');
    return;
  }
  const result = await ImagePicker.launchCameraAsync({ quality: 0.8 });
  if (result.canceled) return;
  setAvatar(result.assets?.[0]?.uri);
};

¿Cómo elegir desde galería o tomar foto?

  • Galería: abrir el selector nativo y actualizar el avatar si no se cancela.
  • Cámara: solicitar permisos, lanzar la cámara y guardar el resultado si continúa.
  • Fallback: si no hay imagen elegida, mantener el avatar actual.

¿Cómo se construye el componente avatar reutilizable?

Se exporta un componente que recibe tamaño, nombre, url y un onPress. Usa Image con la prop source para la URL, estilos con borderRadius para el círculo perfecto y un util para mostrar iniciales cuando no hay imagen. El botón reutilizable envuelve el contenido para permitir pulsar y abrir la galería o la cámara.

// utils: obtener iniciales del nombre
const getInitials = (name = '') => name.trim().split(' ').map(p => p[0]).slice(0, 2).join('').toUpperCase();

export function Avatar({ size = 96, name = '', url, onPress }) {
  const circle = { width: size, height: size, borderRadius: size / 2 };
  return (
    <Button onPress={onPress}>
      <View style={[{ alignItems: 'center', justifyContent: 'center' }, circle]}>
        {url ? (
          <Image source={{ uri: url }} style={circle} />
        ) : (
          <Text style={{ fontWeight: '700' }}>{getInitials(name)}</Text>
        )}
      </View>
    </Button>
  );
}

¿Cómo mostrar iniciales si no hay imagen?

  • Verificar si la url existe.
  • Si no existe: renderizar las iniciales del nombre.
  • Mantener accesible el onPress para cambiar el avatar cuando se desee.

¿Cómo gestionar el perfil: estados, guardado y generación con IA?

Se consume un contexto con useProfile y se crean useState para name, role e indicadores de carga. Con useEffect se hace la primera carga del perfil (name y role). El botón de guardar llama al servicio de update desde el contexto, usa textos por defecto cuando faltan campos y muestra una alerta tras guardar. Además, existe una función asíncrona para generar un avatar con IA a partir del nombre como semilla.

// estados locales
const [name, setName] = useState('');
const [role, setRole] = useState('');
const [busy, setBusy] = useState(false);
const { profile, updateProfile } = useProfile();
const [avatar, setAvatar] = useState(profile?.avatar);

// primera carga de datos
useEffect(() => {
  setName(profile?.name);
  setRole(profile?.role);
}, [profile?.name, profile?.role]);

// guardar cambios
const save = async () => {
  setBusy(true);
  await updateProfile({
    name: name || 'sin nombre',
    role: role || 'sin rol',
    avatar,
  });
  setBusy(false);
  alert('datos guardados');
};

// generar avatar con IA
const generateWithAI = async () => {
  setBusy(true);
  const url = await service.generateAvatarWithAI(name);
  setAvatar(url);
  setBusy(false);
};

¿Cómo se organiza la interfaz del perfil?

  • KeyboardAvoidingView: evita que el teclado oculte elementos.
  • Platform para ajustar comportamiento en iOS.
  • ScrollView: contenido breve sin listas extensas.
  • TextInput: controla name y role con useState.
  • Botones: tomar foto, elegir de galería, generar con IA y guardar.
<KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : undefined}>
  <ScrollView>
    <View style={{ alignItems: 'center', margin: 16 }}>
      <Avatar name={name} url={avatar} onPress={chooseFromGallery} />
    </View>

    <View>
      <Text>nombre</Text>
      <TextInput value={name} onChangeText={setName} style={styles.input} />

      <Text>profesión</Text>
      <TextInput value={role} onChangeText={setRole} style={styles.input} />
    </View>

    <Button title="tomar foto" onPress={takePhoto} />
    <Button title="elegir de galería" onPress={chooseFromGallery} />
    <PrimaryButton title="generar con ia" onPress={generateWithAI} />
    <PrimaryButton title="guardar" onPress={save} />
  </ScrollView>
</KeyboardAvoidingView>
// estilos mínimos del input
const styles = {
  input: { borderWidth: 1, borderRadius: 8, padding: 10, marginVertical: 8 },
};

¿Cómo se habilita el acceso global a los datos?

  • Agregar el provider del perfil en la raíz de la app.
  • Así, todas las pantallas consumen el contexto sin pasar props.
<ProfileProvider>
  <App />
</ProfileProvider>
  • Los datos se almacenan en el dispositivo: permanecen al regresar.
  • Puedes sobrescribir el avatar generando otro o eligiendo una nueva imagen.
  • La experiencia se mantiene fluida gracias a un loader cuando el perfil está cargando.

¿Te gustaría ver variantes de estilos o flujos de permisos? Deja un comentario con tus dudas o ideas para seguir iterando este perfil con avatar y IA.