Flutter Web: Covert ui.Image to ImageProvider

Issue

I’m using CustomPainter to draw something to a canvas.

Later I want to display that drawing in an Image widget (which takes an image parameter of type ImageProvider<Object>).

After some research I have found out a way to get there in basically two steps:

  1. create a ui.Image object from the canvas drawing
  2. convert that ui.Image to an ImageProvider that can be passed to an Image widget
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:bitmap/bitmap.dart';

class MyCustomPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    //drawing some stuff here
    //canvas.drawRect(...);
  }
  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;

  Future<ui.Image> getImage() {
    final ui.PictureRecorder recorder = ui.PictureRecorder();
    try {
      //500 x 500 pixels
      paint(Canvas(recorder), const Size(500, 500));
    } catch (e) {
      debugPrint(e.toString());
    }

    final ui.Picture picture = recorder.endRecording();
    final img = picture.toImage(500, 500);
    return img;
  }

  static Future<ImageProvider> getProviderFromImage(ui.Image image) async {
    final ByteData? bytedata = await image.toByteData();
    if (bytedata == null) {
      return Future.error("some error msg");
    }

    final Bitmap bitmap = Bitmap.fromHeadless(
        image.width, image.height, bytedata.buffer.asUint8List());
    final Uint8List headedIntList = bitmap.buildHeaded();
    return MemoryImage(headedIntList);
  }
} 

The problem here is that this won’t work for Flutter Web because the bitmap package does not have web support.

I already tried converting the ByteData to Uint8List without the bitmap package:

  static Future<ImageProvider> getProviderFromImage(ui.Image image) async {
    final ByteData? bytedata = await image.toByteData();
    if (bytedata == null) {
      return Future.error("some error msg");
    }
 
    final Uint8List headedIntList =
        Uint8List.view(bytedata.buffer);  
    return MemoryImage(headedIntList);
  }

But the resulting ImageProvider won’t be accepted by the Image widget:

════════ Exception caught by image resource service
════════════════════════════ The following ImageCodecException was
thrown resolving an image codec: Failed to decode image data. Image
source: encoded image bytes

Solution

My approach actually works if the format parameter of toByteData() is passed:

  static Future<ImageProvider> getProviderFromImage(ui.Image image) async {
    final ByteData? bytedata = await image.toByteData(format: ui.ImageByteFormat.png);
    if (bytedata == null) {
      return Future.error("some error msg");
    }
 
    final Uint8List headedIntList =
        Uint8List.view(bytedata.buffer);  
    return MemoryImage(headedIntList);
  }

Answered By – Lara

Answer Checked By – Katrina (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *