Flutter:如何将Canvas / CustomPainter保存到图像文件?

Jus*_*s10 6 canvas image save dart flutter

我正在尝试从用户那里收集签名并将其保存到图像中。我已经做得足够远,可以在屏幕上绘制了,但是现在我想单击一个按钮以保存到图像并存储在数据库中。

这是我到目前为止的内容:

import 'package:flutter/material.dart';

class SignaturePadPage extends StatefulWidget {
  SignaturePadPage({Key key}) : super(key: key);

  @override
  _SignaturePadPage createState() => new _SignaturePadPage();
}
class _SignaturePadPage extends State<SignaturePadPage> {

  List<Offset> _points = <Offset>[];

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      child: GestureDetector(
        onPanUpdate: (DragUpdateDetails details) {
          setState(() {
            RenderBox referenceBox = context.findRenderObject();
            Offset localPosition =
            referenceBox.globalToLocal(details.globalPosition);
            _points = new List.from(_points)..add(localPosition);
          });
        },
        onPanEnd: (DragEndDetails details) => _points.add(null),
        child: new CustomPaint(painter: new SignaturePainter(_points)),
      ),
    );
  }
}

class SignaturePainter extends CustomPainter {
  SignaturePainter(this.points);
  final List<Offset> points;
  void paint(Canvas canvas, Size size) {
    Paint paint = new Paint()
      ..color = Colors.black
      ..strokeCap = StrokeCap.round
      ..strokeWidth = 5.0;
    for (int i = 0; i < points.length - 1; i++) {
      if (points[i] != null && points[i + 1] != null)
        canvas.drawLine(points[i], points[i + 1], paint);
    }
  }
  bool shouldRepaint(SignaturePainter other) => other.points != points;
}
Run Code Online (Sandbox Code Playgroud)

不知道从那里去哪里...

jsp*_*cal 8

您可以捕获的输出CustomPainterPictureRecorder。将PictureRecorder实例传递给的构造函数Canvas。在Picture通过返回PictureRecorder.endRecording然后可以转化为ImagePicture.toImage。最后,使用提取图像字节Image.toByteData

这是一个示例:https : //github.com/rxlabz/flutter_canvas_to_image

  • 在我的代码中,我使用的是`paint(Canvas canvas,Size size)`,所以没有Canvas构造函数。我将如何结合我的情况? (3认同)

Ara*_*ula 5

在您的小部件中添加渲染的方法

  ui.Image get rendered {
    // [CustomPainter] has its own @canvas to pass our
    // [ui.PictureRecorder] object must be passed to [Canvas]#contructor
    // to capture the Image. This way we can pass @recorder to [Canvas]#contructor
    // using @painter[SignaturePainter] we can call [SignaturePainter]#paint
    // with the our newly created @canvas
    ui.PictureRecorder recorder = ui.PictureRecorder();
    Canvas canvas = Canvas(recorder);
    SignaturePainter painter = SignaturePainter(points: _points);
    var size = context.size;
    painter.paint(canvas, size);
    return recorder.endRecording()
        .toImage(size.width.floor(), size.height.floor());
  }
Run Code Online (Sandbox Code Playgroud)

然后使用状态获取渲染的图像

var image = signatureKey.currentState.rendered
Run Code Online (Sandbox Code Playgroud)

现在,您可以使用生成png图片,toByteData(format: ui.ImageByteFormat.png)并使用存储asInt8List()

var pngBytes = await image.toByteData(format: ui.ImageByteFormat.png)
File('your-path/filename.png')
    .writeAsBytesSync(pngBytes.buffer.asInt8List())
Run Code Online (Sandbox Code Playgroud)

对于完整的示例,有关如何将画布导出为png的信息,请查看此示例 https://github.com/vemarav/signature


War*_*113 5

现有的解决方案对我有用,但我捕获的图像PictureRecorder与屏幕上渲染的图像相比总是模糊的。我最终意识到我可以使用一些基本的 Canvas 技巧来实现这一目标。基本上,创建PictureRecorders后Canvas,将其大小设置为所需比例的倍数(此处我将其设置为 4x)。那么就canvas.scale这样吧。Boom - 与现代分辨率的屏幕上显示的图像相比,您生成的图像不再模糊!

您可能希望_overSampleScale将打印或可能放大/扩展的图像的值调高,或者如果您大量使用此值并希望提高图像预览加载性能,则可能需要调低该值。在屏幕上使用它时,您需要使用实际宽度和高度来约束您的Widget Image.memory就像其他解决方案一样。理想情况下,这个数字应该是 Flutter 假“像素”(即捕获的像素)中的 DPI 与屏幕的实际 DPI之间的比率。ContainerPictureRecorder

static const double _overSampleScale = 4;
Future<ui.Image> get renderedScoreImage async {
    final recorder = ui.PictureRecorder();
    Canvas canvas = Canvas(recorder);
    final size = Size(widget.width * _overSampleScale, widget.height * _overSampleScale);
    final painter = SignaturePainter(points: _points);
    canvas.save();
    canvas.scale(_overSampleScale);
    painter.paint(canvas, size);
    canvas.restore();
    final data = recorder.endRecording()
      .toImage(size.width.floor(), size.height.floor());
    return data;
  }
Run Code Online (Sandbox Code Playgroud)