pickFromGallery static method

Future<String?> pickFromGallery()

Pick an image from the gallery, preserving its EXIF GPS metadata.

On Android the pick is handled natively (ACTION_PICK on the MediaStore images collection + MediaStore.setRequireOriginal() behind a runtime ACCESS_MEDIA_LOCATION request) so the location tags survive the Android 10+ media redaction that silently strips GPS from ImagePicker results. If the native pick fails it falls back to ImagePicker. iOS and other platforms use ImagePicker directly.

Note: the returned image is no longer auto-scrubbed — valid GPS is left intact. Use removeGps explicitly if you need to strip it.

Implementation

static Future<String?> pickFromGallery() async {
  if (Platform.isAndroid) {
    try {
      // null == user cancelled.
      return await _channel.invokeMethod<String>('pickImageWithGps');
    } on PlatformException catch (e) {
      // Only fall back to ImagePicker when the native picker could not launch
      // at all. Never re-open a second picker after the user already chose an
      // image (e.g. on a copy error) — that caused a double-pick.
      if (e.code == 'NO_PICKER' || e.code == 'NO_ACTIVITY') {
        debugPrint('Native picker unavailable, using ImagePicker: $e');
        try {
          final picker = ImagePicker();
          final picked = await picker.pickImage(source: ImageSource.gallery);
          return picked?.path;
        } catch (e) {
          debugPrint('ImagePicker fallback error: $e');
          return null;
        }
      }
      debugPrint('pickImageWithGps error: $e');
      return null;
    } catch (e) {
      debugPrint('pickFromGallery error: $e');
      return null;
    }
  }

  try {
    final picker = ImagePicker();
    final picked = await picker.pickImage(source: ImageSource.gallery);
    return picked?.path;
  } catch (e) {
    debugPrint('pickFromGallery error: $e');
    return null;
  }
}