captureSelection function

Future captureSelection({
  1. required GlobalKey<State<StatefulWidget>> repaintBoundaryKey,
  2. required Rect selection,
  3. required double pixelRatio,
  4. required SelectionOutputType outputType,
})

Captures a portion of a widget defined by a GlobalKey and a selection Rect.

This function is optimized to perform expensive encoding operations in a background isolate to avoid blocking the UI thread.

Implementation

Future<dynamic> captureSelection({
  required GlobalKey repaintBoundaryKey,
  required Rect selection,
  required double pixelRatio,
  required SelectionOutputType outputType,
}) async {
  try {
    final boundary =
        repaintBoundaryKey.currentContext?.findRenderObject()
            as RenderRepaintBoundary?;
    if (boundary == null) return null;

    // Capture the full image of the RepaintBoundary
    final fullImage = await boundary.toImage(pixelRatio: pixelRatio);

    // The selection Rect is in logical pixels; convert to physical pixels for cropping.
    final cropRect = Rect.fromLTWH(
      selection.left * pixelRatio,
      selection.top * pixelRatio,
      selection.width * pixelRatio,
      selection.height * pixelRatio,
    );

    // Use a PictureRecorder to crop the image
    final recorder = ui.PictureRecorder();
    final canvas = Canvas(recorder);
    canvas.drawImageRect(
      fullImage,
      cropRect,
      Rect.fromLTWH(0, 0, cropRect.width, cropRect.height),
      Paint(),
    );

    // Create the final cropped image
    final croppedImage = await recorder.endRecording().toImage(
      cropRect.width.round(),
      cropRect.height.round(),
    );

    // Convert to byte data (this part is relatively fast)
    final byteData = await croppedImage.toByteData(
      format: ui.ImageByteFormat.png,
    );

    // Clean up image resources
    fullImage.dispose();
    croppedImage.dispose();

    if (byteData == null) return null;

    final imageBytes = byteData.buffer.asUint8List();

    // --- Optimization happens here ---

    if (outputType == SelectionOutputType.bytes) {
      // If the user just wants the bytes, we're done. This is fast.
      return imageBytes;
    } else {
      // If the user wants base64, run the expensive encoding in an isolate.
      // The `compute` function spawns an isolate, runs our function, and returns the result.
      return await compute(_encodeToBase64InIsolate, imageBytes);
    }
  } catch (e) {
    debugPrint('Error capturing selection: $e');
    return null;
  }
}