Capturing a Canvas as an Image in Flutter Web

1,838

I reported it as a bug. It seems that the only way to do it, at least until they fix it (it's end of May 2020, for future reference), is by doing it using the HTML5 canvas:

import 'dart:html' as html;

final canvas = html.CanvasElement(width: width, height: height);
var context = canvas.context2D;
context.draw...(); // do whatever drawing you need
final blob = await canvas.toBlob('image/jpeg', 0.8);

Converting from blob to byte array can be done with this:

Future<Uint8List> _getBlobData(html.Blob blob) {
  final completer = Completer<Uint8List>();
  final reader = html.FileReader();
  reader.readAsArrayBuffer(blob);
  reader.onLoad.listen((_) => completer.complete(reader.result));
  return completer.future;
}
Share:
1,838
Vasanthakumar V
Author by

Vasanthakumar V

Updated on December 11, 2022

Comments

  • Vasanthakumar V
    Vasanthakumar V over 1 year

    I am trying to capture the content of a Canvas as an Image using PictureRecorder in Flutter Web, but have trouble in doing so

    While experimenting I drew a simple circle and tried recording it, but I keep getting 'null' for the Picture recorded

    import 'package:flutter_web/material.dart';
    import 'package:flutter_web_ui/ui.dart' as ui;
    
    import 'dart:typed_data';
    
    void main() => runApp(App());
    
    class App extends StatelessWidget {
      void generateImage() async {
        final ui.Paint paint = ui.Paint()
          ..style = ui.PaintingStyle.stroke
          ..strokeWidth = 1.0;
        final ui.PictureRecorder recorder = ui.PictureRecorder();
        final ui.Canvas pictureCanvas = ui.Canvas(recorder);
        pictureCanvas.drawCircle(Offset.zero, 20.0, paint);
        final ui.Picture picture = recorder.endRecording();
        ui.Image referenceImage = picture.toImage(50, 50);
        ByteData img =
            await referenceImage.toByteData(format: ui.ImageByteFormat.png);
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            body: Column(
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.all(12.0),
                  child: RaisedButton(
                    child: Text('Generate image'),
                    onPressed: generateImage,
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    This is the error I get in chrome console,

    main.dart:24 Uncaught (in promise) TypeError: Cannot read property 'toByteData' of null
        at generateImage (main.dart:24)
        at generateImage.next (<anonymous>)
        at runBody (dart_sdk.js:22330)
        at Object.async.async (dart_sdk.js:22358)
        at main.App.new.generateImage (main.dart:15)
        at src__material__ink_well.InkWell.new.<anonymous> (main.dart:38)
        at _InkResponseState.new.[_handleTap] (ink_well.dart:511)
        at ink_well.dart:565
        at src__gestures__tap.TapGestureRecognizer.new.invokeCallback (recognizer.dart:169)
        at src__gestures__tap.TapGestureRecognizer.new.[_checkUp] (tap.dart:251)
        at src__gestures__tap.TapGestureRecognizer.new.handlePrimaryPointer (tap.dart:176)
        at src__gestures__tap.TapGestureRecognizer.new.handleEvent (recognizer.dart:439)
        at src__gestures__pointer_router.PointerRouter.new.[_dispatch] (pointer_router.dart:73)
        at src__gestures__pointer_router.PointerRouter.new.route (pointer_router.dart:100)
        at src__widgets__binding.WidgetsFlutterBinding.new.handleEvent (binding.dart:223)
        at src__widgets__binding.WidgetsFlutterBinding.new.dispatchEvent (binding.dart:201)
        at src__widgets__binding.WidgetsFlutterBinding.new.[_handlePointerEvent] (binding.dart:156)
        at src__widgets__binding.WidgetsFlutterBinding.new.[_flushPointerEventQueue] (binding.dart:103)
        at src__widgets__binding.WidgetsFlutterBinding.new.[_handlePointerDataPacket] (binding.dart:87)
        at src__engine.PointerBinding.new.[_onPointerData] (pointer_binding.dart:80)
        at pointer_binding.dart:194
        at HTMLElement.<anonymous> (pointer_binding.dart:135)