getPNGScreenShotWithMetadata method
Future<Map<String, dynamic> ?>
getPNGScreenShotWithMetadata({
- GlobalKey<
State< ? customKey,StatefulWidget> >
Capturar screenshot del widget actual con metadatos de dispositivo (similar a getPNGScreenShot del JS SDK)
Implementation
Future<Map<String, dynamic>?> getPNGScreenShotWithMetadata(
{GlobalKey? customKey}) async {
try {
final key = customKey ?? _appRootKey;
if (key?.currentContext == null) {
ObslyLogger.warn('Cannot capture screenshot: No valid context found');
return null;
}
final renderObject = key!.currentContext!.findRenderObject();
if (renderObject is! RenderRepaintBoundary) {
ObslyLogger.warn(
'Cannot capture screenshot: RenderObject is not a RenderRepaintBoundary, found: ${renderObject.runtimeType}');
return null;
}
final RenderRepaintBoundary boundary = renderObject;
// Obtener información del dispositivo y contexto
final context = key.currentContext!;
final mediaQuery = MediaQuery.of(context);
final deviceInfo = DeviceInfoCollector.instance.getDeviceInfo();
// Calcular pixel ratio dinámico para conseguir ~6KB
final view = View.of(context);
final screenSize = mediaQuery.size;
// Target: ~400x300px para mejor calidad visual
final targetWidth = 400.0;
final targetHeight = 300.0;
// Calcular pixelRatio necesario
final ratioX = targetWidth / screenSize.width;
final ratioY = targetHeight / screenSize.height;
final double pixelRatio = math.min(ratioX, ratioY);
final ui.Image image = await boundary.toImage(pixelRatio: pixelRatio);
// Usar compresión más agresiva para conseguir ~2-3KB (JPEG-like)
final ByteData? byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
if (byteData == null) {
ObslyLogger.error('Failed to convert image to byte data');
return null;
}
final Uint8List imageBytes = byteData.buffer.asUint8List();
// Apply anonymization if enabled
Uint8List finalBytes = imageBytes;
if (_enableAnonymization) {
final anonymizedBytes =
await ObslyScreenshotAnonymizer.instance.anonymizeScreenshot(
imageBytes: imageBytes,
context: context,
devicePixelRatio: pixelRatio,
);
finalBytes = anonymizedBytes ?? imageBytes;
}
// Aplicar compresión simple (reducir colores para simular JPEG)
final Uint8List compressedBytes =
await _compressForUIEvents(finalBytes, image.width, image.height);
// Log del tamaño para verificar target de ~2-3KB
final sizeKB = (compressedBytes.length / 1024).toStringAsFixed(1);
ObslyLogger.debug(
'DEBUGTIMING: 📸 Screenshot size: ${sizeKB}KB (${image.width}x${image.height}px, ratio: ${pixelRatio.toStringAsFixed(2)})');
// Para UI events, usar imagen comprimida
final String base64Image = base64Encode(compressedBytes);
final String fullBase64 = 'data:image/png;base64,$base64Image';
// Crear metadatos del dispositivo para el preview (similar al JS SDK)
final deviceMetadata = {
'devicePixelRatio': view.devicePixelRatio,
'logicalSize': {
'width': mediaQuery.size.width,
'height': mediaQuery.size.height,
},
'physicalSize': {
'width': view.physicalSize.width,
'height': view.physicalSize.height,
},
'deviceInfo': {
'name': deviceInfo.name,
'manufacturer': deviceInfo.manufacturer,
'model': deviceInfo.model,
'osName': deviceInfo.osName,
'osVersion': deviceInfo.osVersion,
'formFactor': deviceInfo.formFactor,
'resolution': deviceInfo.resolution,
'orientation': deviceInfo.orientation,
},
'captureInfo': {
'originalSize': {
'width': image.width,
'height': image.height,
},
'compressedSize': compressedBytes.length,
'compressionRatio': imageBytes.isNotEmpty
? (compressedBytes.length / imageBytes.length)
: 1.0,
'timestamp': DateTime.now().millisecondsSinceEpoch,
}
};
// Validar que el screenshot se generó correctamente
if (base64Image.isNotEmpty) {
ObslyLogger.debug(
'Screenshot with metadata captured successfully: ${imageBytes.length} → ${compressedBytes.length} bytes');
return {
'image': fullBase64,
'metadata': deviceMetadata,
};
} else {
ObslyLogger.error('Failed to capture screenshot');
return null;
}
} catch (e) {
ObslyLogger.error('Error capturing PNG screenshot with metadata: $e');
return null;
}
}