'Cannot take picture on flutter camera because of "unconfigured surface"

I wanted to capture the image and display a preview screen showing the captured image. It worked fine before, but once I added object detection, I receive a group of errors every time I try to take a photo. I think the object detection is interfering with the capturing.

I/flutter (12681): /data/user/0/com.example.iSPY_Cancer/cache/2020-07-26 14:11:55.456052.png
E/CameraDevice-0-LE(12681): submitRequestList - cannot use a surface that wasn't configured
E/MethodChannel#plugins.flutter.io/camera(12681): Failed to handle method call
E/MethodChannel#plugins.flutter.io/camera(12681): java.lang.IllegalArgumentException: Bad argument passed to camera service
E/MethodChannel#plugins.flutter.io/camera(12681):   at android.hardware.camera2.utils.CameraBinderDecorator.throwOnError(CameraBinderDecorator.java:114)
E/MethodChannel#plugins.flutter.io/camera(12681):   at android.hardware.camera2.utils.CameraBinderDecorator$CameraBinderDecoratorListener.onAfterInvocation(CameraBinderDecorator.java:73)
E/MethodChannel#plugins.flutter.io/camera(12681):   at android.hardware.camera2.utils.Decorator.invoke(Decorator.java:81)
E/MethodChannel#plugins.flutter.io/camera(12681):   at java.lang.reflect.Proxy.invoke(Proxy.java:393)
E/MethodChannel#plugins.flutter.io/camera(12681):   at $Proxy1.submitRequestList(Unknown Source)
E/MethodChannel#plugins.flutter.io/camera(12681):   at android.hardware.camera2.impl.CameraDeviceImpl.submitCaptureRequest(CameraDeviceImpl.java:853)
E/MethodChannel#plugins.flutter.io/camera(12681):   at android.hardware.camera2.impl.CameraDeviceImpl.capture(CameraDeviceImpl.java:741)
E/MethodChannel#plugins.flutter.io/camera(12681):   at android.hardware.camera2.impl.CameraCaptureSessionImpl.capture(CameraCaptureSessionImpl.java:179)
E/MethodChannel#plugins.flutter.io/camera(12681):   at io.flutter.plugins.camera.Camera.takePicture(Camera.java:253)
E/MethodChannel#plugins.flutter.io/camera(12681):   at io.flutter.plugins.camera.MethodCallHandlerImpl.onMethodCall(MethodCallHandlerImpl.java:77)
E/MethodChannel#plugins.flutter.io/camera(12681):   at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:226)
E/MethodChannel#plugins.flutter.io/camera(12681):   at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:85)
E/MethodChannel#plugins.flutter.io/camera(12681):   at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:631)
E/MethodChannel#plugins.flutter.io/camera(12681):   at android.os.MessageQueue.nativePollOnce(Native Method)
E/MethodChannel#plugins.flutter.io/camera(12681):   at android.os.MessageQueue.next(MessageQueue.java:323)
E/MethodChannel#plugins.flutter.io/camera(12681):   at android.os.Looper.loop(Looper.java:141)
E/MethodChannel#plugins.flutter.io/camera(12681):   at android.app.ActivityThread.main(ActivityThread.java:5603)
E/MethodChannel#plugins.flutter.io/camera(12681):   at java.lang.reflect.Method.invoke(Native Method)
E/MethodChannel#plugins.flutter.io/camera(12681):   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:774)
E/MethodChannel#plugins.flutter.io/camera(12681):   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:652)
I/flutter (12681): CameraException(error, Bad argument passed to camera service)

Here is the code for image capturing. As seen in the first line of the errors, the image path is successfully printed, which means the code leading up to print(path) is successful, but the rest doesn't work. If anyone has encountered these errors before, please help me. I am really stuck.

void _onCapturePressed(context) async {
    // Take the Picture in a try / catch block. If anything goes wrong,

    // catch the error.

    try {
      // Attempt to take a picture and log where it's been saved

      final path = join(
        // In this example, store the picture in the temp directory. Find

        // the temp directory using the `path_provider` plugin.

        (await getTemporaryDirectory()).path,

        '${DateTime.now()}.png',
      );

      print(path);

      await controller.takePicture(path);

      // If the picture was taken, display it on a new screen

      Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => PreviewImageScreen(imagePath: path),
        ),
      );
    } catch (e) {
      // If an error occurs, log the error to the console.

      print(e);
    }
  }

And this is the code for the entire screen with object detection:

import 'package:camera/camera.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:tflite/tflite.dart';
import 'dart:math' as math;

import '../preview_screen.dart';
import 'models.dart';

typedef void Callback(List<dynamic> list, int h, int w);

class Camera extends StatefulWidget {
  final List<CameraDescription> cameras;  
  final Callback setRecognitions;
  final String model;

  Camera(this.cameras, this.model, this.setRecognitions);

  @override
  _CameraState createState() => new _CameraState();
}

class _CameraState extends State<Camera> {
  double xPosition = 120;
  double yPosition = 150;

  List cameras;
  int selectedCameraIdx;
  String imagePath;

  CameraController controller;
  bool isDetecting = false;

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

    if (widget.cameras == null || widget.cameras.length < 1) {
      print(widget.cameras);
      print('No camera is found');
    } else {
      controller = new CameraController(
        widget.cameras[0],
        ResolutionPreset.high,
      );
      controller.initialize().then((_) {
        if (!mounted) {
          return;
        }
        setState(() {});

        controller.startImageStream((CameraImage img) {
          if (!isDetecting) {
            isDetecting = true;

            int startTime = new DateTime.now().millisecondsSinceEpoch;

            if (widget.model == mobilenet) {
              Tflite.runModelOnFrame(
                bytesList: img.planes.map((plane) {
                  return plane.bytes;
                }).toList(),
                imageHeight: img.height,
                imageWidth: img.width,
                numResults: 2,
              ).then((recognitions) {
                int endTime = new DateTime.now().millisecondsSinceEpoch;
                print("Detection took ${endTime - startTime}");

                widget.setRecognitions(recognitions, img.height, img.width);

                isDetecting = false;
              });
            } else if (widget.model == posenet) {
              Tflite.runPoseNetOnFrame(
                bytesList: img.planes.map((plane) {
                  return plane.bytes;
                }).toList(),
                imageHeight: img.height,
                imageWidth: img.width,
                numResults: 2,
              ).then((recognitions) {
                int endTime = new DateTime.now().millisecondsSinceEpoch;
                print("Detection took ${endTime - startTime}");

                widget.setRecognitions(recognitions, img.height, img.width);

                isDetecting = false;
              });
            } else {
              Tflite.detectObjectOnFrame(
                bytesList: img.planes.map((plane) {
                  return plane.bytes;
                }).toList(),
                model: widget.model == yolo ? "YOLO" : "SSDMobileNet",
                imageHeight: img.height,
                imageWidth: img.width,
                imageMean: widget.model == yolo ? 0 : 127.5,
                imageStd: widget.model == yolo ? 255.0 : 127.5,
                numResultsPerClass: 1,
                threshold: widget.model == yolo ? 0.2 : 0.4,
              ).then((recognitions) {
                int endTime = new DateTime.now().millisecondsSinceEpoch;
                print("Detection took ${endTime - startTime}");

                widget.setRecognitions(recognitions, img.height, img.width);

                isDetecting = false;
              });
            }
          }
        });
      });
    }
  }

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

  @override
  Widget build(BuildContext context) {
    if (controller == null || !controller.value.isInitialized) {
      return Container();
    }

    var tmp = MediaQuery.of(context).size;
    var screenH = math.max(tmp.height, tmp.width);
    var screenW = math.min(tmp.height, tmp.width);
    tmp = controller.value.previewSize;
    var previewH = math.max(tmp.height, tmp.width);
    var previewW = math.min(tmp.height, tmp.width);
    var screenRatio = screenH / screenW;
    var previewRatio = previewH / previewW;

    return Scaffold(
      appBar: AppBar(
          backgroundColor: Colors.lightBlue,
          centerTitle: true,
          elevation: 5,
          title: Text("Pathomatic")),
      body: Container(
        decoration: new BoxDecoration(color: Colors.black),
        child: SafeArea(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              Expanded(
                flex: 2,
                child: new Stack(
                  children: <Widget>[
                    new Container(
                      alignment: Alignment.center,
                      child: _cameraPreviewWidget(context),
                    ),
                    new Align(
                      alignment: Alignment.center,
                      child: GestureDetector(
                          child: Stack(children: <Widget>[
                            Positioned(
                              top: yPosition,
                              left: xPosition,
                              child: Container(
                                // padding: EdgeInsets.only(top: 200.0, left: 20.0),
                                height: MediaQuery.of(context).size.height / 3,
                                width: MediaQuery.of(context).size.width / 3,
                                child:
                                    Image.asset('assets/images/crosshair.png'),
                              ),
                            ),
                          ]),
                          onPanUpdate: (tapInfo) {
                            setState(() {
                              xPosition += tapInfo.delta.dx;
                              yPosition += tapInfo.delta.dy;
                            });
                          }),
                    ),
                  ],
                ),
              ),
              SizedBox(
                height: 10.0,
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  _cameraTogglesRowWidget(),
                  _captureControlRowWidget(context),
                  Spacer()
                ],
              ),
              SizedBox(height: 10.0)
            ],
          ),
        ),
      ),
    );
  }

  Widget _cameraPreviewWidget(context) {
    final size = MediaQuery.of(context).size;

    if (!controller.value.isInitialized) {
      return Container();
    }

    return ClipRect(
      child: Container(
        child: Transform.scale(
          scale: controller.value.aspectRatio / size.aspectRatio,
          child: Center(
            child: AspectRatio(
              aspectRatio: controller.value.aspectRatio,
              child: CameraPreview(controller),
            ),
          ),
        ),
      ),
    );
  }

  /// Display the control bar with buttons to take pictures

  Widget _captureControlRowWidget(context) {
    return Expanded(
      child: Align(
        alignment: Alignment.center,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          mainAxisSize: MainAxisSize.max,
          children: [
            FloatingActionButton(
                child: Icon(Icons.camera_alt),
                backgroundColor: Colors.lightBlue,
                onPressed: () {
                  _onCapturePressed(context);
                })
          ],
        ),
      ),
    );
  }

  /// Display a row of toggle to select the camera (or a message if no camera is available).

  Widget _cameraTogglesRowWidget() {
    if (cameras == null || cameras.isEmpty) {
      return Spacer();
    }

    CameraDescription selectedCamera = cameras[selectedCameraIdx];

    CameraLensDirection lensDirection = selectedCamera.lensDirection;

    ///         CHNAGE THIS TO INCREASE MAGNIFICATION
    ///
    return Expanded(
      child: Align(
        alignment: Alignment.centerLeft,
        child: FlatButton.icon(
            onPressed: _onSwitchCamera,
            icon: Icon(_getCameraLensIcon(lensDirection), color: Colors.white),
            label: Text(
              "10x",
              // "${lensDirection.toString().toUpperCase().substring(lensDirection.toString().indexOf('.') + 1)}",
              style: TextStyle(color: Colors.white),
            )),
      ),
    );
  }

  IconData _getCameraLensIcon(CameraLensDirection direction) {
    switch (direction) {
      case CameraLensDirection.back:
        return Icons.camera_rear;

      case CameraLensDirection.front:
        return Icons.camera_front;

      case CameraLensDirection.external:
        return Icons.camera;

      default:
        return Icons.device_unknown;
    }
  }

  void _onSwitchCamera() {
    selectedCameraIdx =
        selectedCameraIdx < cameras.length - 1 ? selectedCameraIdx + 1 : 0;

    CameraDescription selectedCamera = cameras[selectedCameraIdx];

    // _initCameraController(selectedCamera);
  }

  void _onCapturePressed(context) async {
    // Take the Picture in a try / catch block. If anything goes wrong,

    // catch the error.

    try {
      // Attempt to take a picture and log where it's been saved

      final path = join(
        // In this example, store the picture in the temp directory. Find

        // the temp directory using the `path_provider` plugin.

        (await getTemporaryDirectory()).path,

        '${DateTime.now()}.png',
      );

      print(path);

      await controller.takePicture(path);

      // If the picture was taken, display it on a new screen

      Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => PreviewImageScreen(imagePath: path),
        ),
      );
    } catch (e) {
      // If an error occurs, log the error to the console.

      print(e);
    }
  }



  // CAMERA EXCEPTION METHOD
  
  // void _showCameraException(CameraException e) {
  //   String errorText = 'Error: ${e.code}\nError Message: ${e.description}';

  //   print(errorText);

  //   print('Error: ${e.code}\n${e.description}');
  // }
}


Solution 1:[1]

This seems to be a bug and looks like was caused by plugin migration as mentioned on this issue ticket. The issue should now be fixed by upgrading to the latest version of the camera plugin.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Omatt