Curso de Integración Módulos Nativos iOS/Android para Flutter

Controlador de cámara iOS con FlutterError

Curso de Integración Módulos Nativos iOS/Android para Flutter

Contenido del curso

Acceso a sensores y cámara

Controlador de cámara iOS con FlutterError

Resumen

Capturar una foto desde la cámara nativa de iOS con Flutter implica más que mostrar el picker: necesitas controlar qué pasa cuando el usuario toma la foto, cuando la cancela y cómo devolver esa información a tu canal de comunicación. Aquí construimos el controlador completo del UIImagePickerController, gestionamos el almacenamiento temporal de la imagen y emitimos errores claros hacia Flutter usando FlutterError.

¿Cómo crear el controlador del UIImagePickerController en iOS?

El primer paso es preparar la variable que va a guardar el resultado de la captura y luego implementar la función delegate que iOS dispara cuando el usuario termina de seleccionar una imagen.

Declaras un imageCaptureResult de tipo FlutterResult para devolver la respuesta a Flutter cuando la captura termine. Después implementas la función imagePickerController(_:didFinishPickingMediaWithInfo:), que recibe dos parámetros: el picker de tipo UIImagePickerController y un diccionario info con clave UIImagePickerController.InfoKey. Ese info trae todos los datos de la imagen seleccionada, incluyendo URL y path.

Dentro del cuerpo, lo primero es cerrar el picker con dismiss(animated: true, completion: nil) para replicar el comportamiento natural de las apps en iOS.

¿Qué hace didFinishPickingMediaWithInfo? Es el método delegate que iOS invoca cuando el usuario finaliza la selección de una imagen en el picker. Recibe el controlador y un diccionario con la información del archivo capturado.

¿Cómo guardar la imagen capturada con compresión JPEG?

Una vez cerrado el picker, validas que exista una imagen real dentro del diccionario info accediendo a la llave .originalImage y casteándola a UIImage.

Con la imagen ya en memoria, sigues estos pasos para almacenarla:

  • Comprimir la imagen con image.jpegData(compressionQuality: 0.8). El valor 0.8 define la calidad del JPEG en escala double de 0 a 1.
  • Construir el path temporal usando FileManager.default.temporaryDirectory.appendingPathComponent("captureImage").
  • Escribir el archivo con try imageData?.write(to: path) para persistirlo en disco.

Esta secuencia te entrega un archivo físico que puedes referenciar después por su ruta. El uso del temporaryDirectory es clave: es el espacio que iOS asigna para archivos efímeros, así que no contaminas el almacenamiento permanente del usuario.

Cuando todo sale bien, completas el flujo invocando imageCaptureResult con el path de la imagen como argumento. Así Flutter recibe la ruta exacta donde quedó guardada la foto.

¿Qué es compressionQuality en jpegData? Es un valor double entre 0 y 1 que define la calidad del JPEG resultante. Un 0.8 ofrece buena calidad visual con un tamaño de archivo razonable.

¿Cómo manejar errores cuando el usuario cancela la cámara?

El flujo feliz es solo la mitad del trabajo. Necesitas cubrir dos escenarios de error: cuando no se logra extraer la imagen del info y cuando el usuario presiona explícitamente el botón de cancelar.

Error cuando no se captura la imagen

Si la condición que valida la imagen falla, devuelves a Flutter un FlutterError con estos parámetros:

  • code: "imageError" para distinguirlo de otros errores del flujo.
  • message: "no image captured" para describir qué ocurrió.
  • details: nil porque no hay información adicional relevante.

Este error viaja por el mismo imageCaptureResult y Flutter lo recibe como una excepción manejable.

Cancelación explícita desde el botón Cancel

Para cuando el usuario toca Cancel en la UI nativa de iOS, implementas la función imagePickerControllerDidCancel(_:). Esta es más simple: solo cierra el picker con dismiss(animated: true, completion: nil) y devuelve un FlutterError con código "cancelado" y un mensaje indicando que el usuario canceló la operación.

Diferenciar ambos errores con códigos distintos (imageError vs cancelado) te permite reaccionar de forma específica desde el lado Dart, por ejemplo mostrando un toast solo cuando hubo un fallo real y no cuando el usuario simplemente cambió de opinión.

¿Cuándo se dispara imagePickerControllerDidCancel? Cuando el usuario presiona el botón de cancelar dentro del UIImagePickerController de iOS. Es la señal nativa de que la operación se abortó intencionalmente.

¿Por qué importa devolver errores tipados a Flutter?

El FlutterError es el puente estándar entre código nativo y Dart cuando algo no sale como esperabas. Al construirlo con code, message y details, le das a tu capa Flutter información estructurada para tomar decisiones.

En este flujo concreto, distingues tres estados posibles desde el MethodChannel:

  1. Éxito: devuelves el path del archivo JPEG guardado en el directorio temporal.
  2. Imagen no capturada: devuelves imageError cuando la información llega vacía o mal formada.
  3. Usuario cancela: devuelves cancelado cuando se interrumpe el flujo por acción del usuario.

Con esta arquitectura, tu plugin de cámara responde de forma predecible a cualquier interacción y deja a la capa Flutter libre para mostrar la UI adecuada en cada caso. ¿Ya pensaste cómo vas a representar cada estado en tu pantalla? Comenta cómo lo manejarías tú.