Cámara y galería en React Native

Clase 21 de 22Curso 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.

      Cámara y galería en React Native