Skip to content

Instantly share code, notes, and snippets.

@jezell
Created December 6, 2024 07:50
Show Gist options
  • Save jezell/584494ab0edcd89cd1275a48d25c63dc to your computer and use it in GitHub Desktop.
Save jezell/584494ab0edcd89cd1275a48d25c63dc to your computer and use it in GitHub Desktop.
Fix this bug

Issue Explanation

The reported error involves a runtime crash when the vector_graphics package attempts to draw an <image> element from an SVG. The stack trace suggests a TypeError is happening during onDrawImage, likely due to the decoding of image bytes into a Dart/Flutter UI image object not producing the expected type.

Prior to Flutter 3.10, decodeImageFromList was the conventional method to decode images from byte arrays. However, with updated Flutter versions and web compatibility changes, decodeImageFromList on the web may not return the expected ui.Image type, causing type errors at runtime. Instead, it's recommended to use the newer image decoding approach that involves ImmutableBuffer, ImageDescriptor, and Codec.

Root Cause

  • The onDrawImage method in FlutterVectorGraphicsListener is expecting a ui.Image, but the decoding method used might be returning something else or failing silently, leading to a TypeError.
  • If the code previously relied on decodeImageFromList, this can fail on web because that method is not fully supported. Using the newer ui.ImageDescriptor-based pipeline ensures a proper ui.Image object is obtained.

Proposed Fix

Replace any usage of decodeImageFromList with the newer decoding API. This ensures compatibility across all platforms and avoids type issues. For example, in listener.dart (around line 773 where onDrawImage is implemented), update the image decoding logic:

Before (old approach):

ui.decodeImageFromList(imageBytes, (ui.Image image) {
  // ... code to use image ...
});

After (new, robust approach):

Future<void> onDrawImage(Uint8List imageBytes, double x, double y, double width, double height, Rect? clipRect) async {
  if (imageBytes.isEmpty) {
    // No image data, just return.
    return;
  }

  // Use the newer decoding pipeline:
  final ui.ImmutableBuffer buffer = await ui.ImmutableBuffer.fromUint8List(imageBytes);
  final ui.ImageDescriptor descriptor = await ui.ImageDescriptor.encoded(buffer);
  final ui.Codec codec = await descriptor.instantiateCodec();
  final ui.FrameInfo frameInfo = await codec.getNextFrame();
  final ui.Image image = frameInfo.image;

  // Now that we have a proper ui.Image, we can safely draw it:
  paintImage(
    canvas: canvas,
    image: image,
    rect: Rect.fromLTWH(x, y, width, height),
    fit: BoxFit.contain,
  );
}

Additional Notes:

  • Ensure that canvas and related variables are in scope when drawing the image.
  • If the existing code uses a listener or callback-based approach, adapt it to the async/await style used above.
  • Handle any potential null checks or exceptions during decoding gracefully.

By making this change, you ensure that images embedded in your SVGs decode reliably into ui.Image instances, preventing the TypeError and runtime crashes previously encountered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment