如何在Flutter中拍摄CameraPreview的屏幕截图?

时间:2018-06-29 14:19:33

标签: flutter flutter-layout

我需要快速访问我的新Flutter应用程序的CameraPreview数据。 如果我用controller.takePicture(filePath)拍照,则需要几秒钟的时间将文件保存到磁盘上,以便我可以访问它。 我不需要高质量的图像,因此获得与手机屏幕显示分辨率相同的分辨率会很好。我已经尝试过this method,但是它只捕获我自己绘制的叠加层和小部件,而不捕获Camera预览数据。 这是使用this method时问题的最小工作示例:

https://www.youtube.com/watch?v=CWBLjCwH5c0

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'dart:async';
import 'package:camera/camera.dart';
import 'dart:ui' as ui;

List<CameraDescription> cameras;

Future<Null> main() async {
  debugPaintSizeEnabled = false;
  debugPaintLayerBordersEnabled = false;
  try {
    cameras = await availableCameras();
  } on CameraException catch (e) {
    logError(e.code, e.description);
  }

  runApp(new MaterialApp(
    home: new MyApp(),
  ));
}

void logError(String code, String message) =>
    print('Error: $code\nError Message: $message');

class MyApp extends StatefulWidget {
  @override
  _State createState() => new _State();
}

class _State extends State<MyApp> {
  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
  static GlobalKey previewContainer = new GlobalKey();
  CameraController controller;
  ui.Image image;
  Offset blueSquareOffset = new Offset(10.0, 10.0);

  @override
  void initState() {
    super.initState();

    controller = new CameraController(cameras[0], ResolutionPreset.low);
    controller.initialize().then((_) {
      if (!mounted) {
        return;
      }
      setState(() {});
    });
  }

  @override
  void dispose() {
    controller?.dispose();
    super.dispose();
  }

  void _getScreenShotImage() async {
    _capturePng();
    image = await _capturePng();
    debugPrint("im height: ${image.height}, im width: ${image.width}");
    setState(() {});
  }

  Future<ui.Image> _capturePng() async {
    RenderRepaintBoundary boundary =
        previewContainer.currentContext.findRenderObject();
    ui.Image image = await boundary.toImage();
    return image;
  }

  /// Display the preview from the camera (or a message if the preview is not available).
  Widget _cameraPreviewWidget() {
    if (controller == null || !controller.value.isInitialized) {
      return const Text('Camera is initialising...');
    } else {
      return Center(
        child: new AspectRatio(
            aspectRatio: controller.value.aspectRatio,
            child: RepaintBoundary(
              //key: previewContainer,
              child: new GestureDetector(
                child: new CameraPreview(controller),
              ),
            )),
      );
    }
  }

  void _moveBlueSquare(DragUpdateDetails details) {
    setState(() {
      _getScreenShotImage();
      blueSquareOffset = blueSquareOffset + details.delta;
    });
  }

  Widget _blueSquare() {
    return new Positioned(
        top: blueSquareOffset.dy,
        left: blueSquareOffset.dx,
        width: 50.0,
        height: 50.0,
        child: new GestureDetector(
            onPanUpdate: _moveBlueSquare,
            child: new Container(
              color: Color.fromARGB(255, 10, 10, 255),
            )));
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        key: _scaffoldKey,
        appBar: new AppBar(
          title: new Text('Render Boundry Screenshot Error Example'),
        ),
        body: RepaintBoundary(
            key: previewContainer,
            child: new Container(
                padding: new EdgeInsets.all(0.0),
                margin: new EdgeInsets.all(0.0),
                child: new RepaintBoundary(
                    //key: previewContainer,
                    child: new Stack(
                  fit: StackFit.expand,
                  overflow: Overflow.clip,
                  children: <Widget>[
                    new Column(
                      mainAxisSize: MainAxisSize.max,
                      crossAxisAlignment: CrossAxisAlignment.stretch,
                      children: <Widget>[
                        new Expanded(
                            child: new Stack(children: <Widget>[
                          new RepaintBoundary(
                            child: new Container(child: _cameraPreviewWidget()),
                          ),
                          _blueSquare(),
                        ])),
                        new Expanded(
                          child: new Container(
                              //color: Color.fromARGB(50, 50, 50, 50),
                              child: new CustomPaint(
                            painter: new RectanglePainter(image),
                          )),
                        )
                      ],
                    ),
                  ],
                )))));
  }
}

class RectanglePainter extends CustomPainter {
  RectanglePainter(this.image);

  ui.Image image;

  @override
  void paint(Canvas canvas, Size size) {
    if (image == null) {
      canvas.drawRect(
          new Rect.fromLTRB(100.0, 50.0, 300.0, 200.0),
          new Paint()
            ..color = Color.fromARGB(255, 50, 50, 255)
            ..style = PaintingStyle.stroke
            ..strokeWidth = 6.0);
    } else {
      canvas.drawImage(image, new Offset(0.0, 0.0), new Paint());
    }
  }

  @override
  bool shouldRepaint(RectanglePainter old) {
    return true;
  }
}

任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:2)

更新:2020年7月

目前,在Flutter上获取CameraPreview的屏幕截图的最佳方法是使用native_screenshot软件包。

您可以简单地使用

Future<void> getScreenshot() async{
    String path = await NativeScreenshot.takeScreenshot();
    print(path);
  }

保存屏幕截图。请参阅程序包页面以获取其他权限和设置。在性能方面,它似乎有点慢(我的2018年小米A1上为500ms-1s)。我目前正在寻找提高屏幕捕获速度的方法。

答案 1 :(得分:1)

有一种方法可以从布局中获取快照。试试这个:

Android get Image of Main Relativelayout from xml layout?

希望对您有所帮助。