validateXFile static method

Future<({String? error, bool isValid})> validateXFile(
  1. XFile xFile
)

Validate XFile (from image_picker)

Returns true if valid, false otherwise with error message

Implementation

static Future<({bool isValid, String? error})> validateXFile(
  XFile xFile,
) async {
  try {
    // Get file size by reading bytes
    final bytes = await xFile.readAsBytes();
    final fileSize = bytes.length;

    const maxSize = 10 * 1024 * 1024; // 10MB
    if (fileSize > maxSize) {
      return (isValid: false, error: 'File size exceeds 10MB');
    }

    if (fileSize == 0) {
      return (isValid: false, error: 'File is empty');
    }

    // Check file extension and mimeType
    bool isValidFormat = false;
    final validExtensions = ['jpg', 'jpeg', 'png', 'heic', 'heif', 'webp'];

    // First, try to validate by extension
    try {
      final path = xFile.path;
      if (path.isNotEmpty && path.contains('.')) {
        final extension = path.split('.').last.toLowerCase();
        if (validExtensions.contains(extension)) {
          isValidFormat = true;
        }
      }
    } catch (e) {
      // Extension check failed, will try mimeType
    }

    // If extension check didn't pass, try mimeType
    if (!isValidFormat) {
      try {
        final mimeType = xFile.mimeType;
        if (mimeType != null) {
          // Check if it's an image mime type
          if (mimeType.startsWith('image/')) {
            // Extract format from mimeType (e.g., "image/png" -> "png")
            final mimeFormat = mimeType.split('/').last.toLowerCase();
            if (validExtensions.contains(mimeFormat) ||
                mimeFormat == 'png' ||
                mimeFormat == 'jpeg' ||
                mimeFormat == 'jpg') {
              isValidFormat = true;
            }
          }
        }
      } catch (e) {
        // mimeType check also failed
      }
    }

    // If still not valid, check by file signature (magic numbers)
    if (!isValidFormat) {
      try {
        // Check first few bytes for image signatures
        final signature = bytes.take(8).toList();

        // PNG signature: 89 50 4E 47 0D 0A 1A 0A
        if (signature.length >= 8 &&
            signature[0] == 0x89 &&
            signature[1] == 0x50 &&
            signature[2] == 0x4E &&
            signature[3] == 0x47) {
          isValidFormat = true;
        }
        // JPEG signature: FF D8 FF
        else if (signature.length >= 3 &&
            signature[0] == 0xFF &&
            signature[1] == 0xD8 &&
            signature[2] == 0xFF) {
          isValidFormat = true;
        }
        // WebP signature: Check for "RIFF" and "WEBP"
        else if (bytes.length >= 12 &&
            String.fromCharCodes(bytes.take(4)) == 'RIFF' &&
            String.fromCharCodes(bytes.skip(8).take(4)) == 'WEBP') {
          isValidFormat = true;
        }
      } catch (e) {
        // Signature check failed
      }
    }

    if (!isValidFormat) {
      return (
        isValid: false,
        error:
            'Invalid file format. Supported: ${validExtensions.join(", ")}',
      );
    }

    return (isValid: true, error: null);
  } catch (e) {
    return (isValid: false, error: 'Error validating file: ${e.toString()}');
  }
}