Flutter : Disposed CameraController, buildPreview() was called on a disposed CameraController

Viewed 1378

How to initialized and dispose camera controller properly. I am using camera: ^0.9.4+5

Along with many

other errors

I am getting this error on switch camera button click:

======== Exception caught by widgets library =======================================================
The following CameraException was thrown building ValueListenableBuilder<CameraValue>(dirty, state: _ValueListenableBuilderState<CameraValue>#47f17):
CameraException(Disposed CameraController, buildPreview() was called on a disposed CameraController.)

Here is the complete code:

class VideoRecorderScreen extends StatefulWidget {
  @override
  _VideoRecorderScreenState createState() {
    return _VideoRecorderScreenState();
  }

  //final ApplicantData applicantData;
  final String videorec;
 

  VideoRecorderScreen(this.applicationId, this.name, this.description,
      this.descriptionHindi, this.videorec,
      {this.isOpenSubsDashboard = false});
}

class _VideoRecorderScreenState extends State<VideoRecorderScreen> {
  CameraController controller;
  XFile videoPath;
  List<CameraDescription> cameras;
  int selectedCameraIdx = 1;
  int videoElapseTime = 25;
  int videoMinElapseTime = 20;
  
  bool visibilityStop = false;
  bool visibilityButtons = false;
  String timer = "00:00";
  CountdownTimer cd;
  bool startRecording = false;

void _reset() {
    videoPath = null;
    _changed(false);
    _changedTimer("00:00");
    _changedButtonVisibility(false);
  }

  void _changed(bool visibility) {
    if (mounted) setState(() {
      visibilityStop = visibility;
    });
  }

  void _changedTimer(String time) {
    if (mounted) setState(() {
      timer = time;
    });
  }

  void _changedButtonVisibility(bool visibility) {
    if (mounted) setState(() {
      visibilityButtons = visibility;
    });
  }

  void _navigateAndDisplaySelection(BuildContext context) async {
    final result = await Navigator.push(
      context,
      MaterialPageRoute(
          builder: (context) => VideoPlayerScreen(
              videoPath: videoPath.path,
              applicationId: widget.applicationId,
              name: widget.name,
              dateTime: selectedDate,
              isOpenSubsDashboard: widget.isOpenSubsDashboard)),
    );

    if (result) {
      _reset();
    }
  }


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

    WidgetsBinding.instance.addPostFrameCallback((_) =>
        showSnackBar('User Validated Successfully! Welcome ' + widget.name));

   
    availableCameras().then((availableCameras) {
      cameras = availableCameras;

      if (cameras.length > 0) {
      
        for (int i = 0; i < cameras.length; i++) {
          if (cameras[i].lensDirection == CameraLensDirection.front) {
            if (mounted) setState(() {
              selectedCameraIdx = i;
            });
          }
        }

        _onCameraSwitched(cameras[selectedCameraIdx]).then((void v) {});
      }
    }).catchError((err) {
      print('Error: $err.code\nError Message: $err.message');
    });
  }


  @override
  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size.width * (2 / 3);
    return WillPopScope(
      onWillPop: _onBackPressed,
      child: Scaffold(
          key: _scaffoldKey,
          appBar: AppBarWidget(
            title: 'Record Your Video',
              actions: [
              IconButton(
                  icon: Icon(
                    Icons.call,
                   
                  ),
                  onPressed: () {
                    ConstantMethods.callToSupport();
                  }),
              AppBarLogo(),
            ],
          ),
          body: Builder(builder: (BuildContext buildContext) {
            scaffold = Scaffold.of(buildContext);
            return Container(
                   child: ListView(
            
              children: <Widget>[
              
           Center(
                                  child: Container(
                                width: size / 1.5,
                                height: size / 1.5,
                                
                              Container(
                                padding: EdgeInsets.all(5.0),
                                color: Colors.grey[850],
                                child: Text(
                                    selectedDate != null ? selectedDate : "",
                                    textAlign: TextAlign.start,
                                    
                              ),
                            ],
                          ),
                        ))
                    : Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: <Widget>[
                          VerticalSpacing(20.0),
                          Padding(
                              padding:
                                  const EdgeInsets.only(left: 8.0, right: 8.0),
                              child: Text(
                                  'Click on video icon to start the recording',
                                  textAlign: TextAlign.justify,
                                 
                          FlatButton(
                         child: Text(' Proceed for Recording '),
                            onPressed: () {
                              if (mounted) setState(() {
                                startRecording = true;
                              });
                            },
                          ),
                          VerticalSpacing(10.0),
                          FlatButton(

                            textColor: ThemeColors.colorPrimary,
                            VerticalSpacing(20.0),
                        ],
                      ),
                startRecording
                    ? Padding(
                        padding: const EdgeInsets.all(5.0),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: <Widget>[
                            _cameraTogglesRowWidget(),
                          
                            _captureControlRowWidget(),
                            Expanded(
                              child: Text("$timer",
                                  textAlign: TextAlign.end,
                                  style: TextStyle(
                                      fontSize: Consts.fontSizeMedium)),
                            ),
                          ],
                        ),
                      )
                    : new Container(),
              
                !visibilityButtons
                    ? Column(
                   
                        children: [
                          Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
                              //alignment: Alignment.bottomRight,
                              children: [
                                IconButton(
                                  icon: const Icon(Icons.info_outline,
                                      size: 25.0),
                                  color: ThemeColors.colorPrimary,
                                  onPressed: () {
                                    showDialog(
                                      context: context,
                                      builder: (BuildContext context) =>
                                          MessageDialog(
                                        title: "ImportantTips",
                                        message: Languages.of(context)
                                            .videoPreInstruction,
                                      ),
                                    );
                                  
                                  },
                                ),
                                ToggleButtons(
                                  borderColor: ThemeColors.colorAccent,
                                  fillColor: Colors.lightBlue[100],
                                  borderWidth: 1,
                                  selectedBorderColor: ThemeColors.colorAccent,
                                  selectedColor: Colors.blue[900],
                                  color: ThemeColors.colorPrimary,
                                  borderRadius: BorderRadius.circular(10),
                                  children: <Widget>[
                                    Padding(
                                      padding: const EdgeInsets.only(
                                          left: 12.0, right: 8.0),
                                      child: Text(
                                        'In English',
                                        textAlign: TextAlign.center,
                                        style: TextStyle(
                                            fontSize: Consts.fontSizeSmall),
                                      ),
                                    ),
                                    Padding(
                                      padding: const EdgeInsets.only(
                                          left: 8.0, right: 8.0),
                                      child: Text(
                                        'हिंदी में       ',
                                        textAlign: TextAlign.center,
                                        style: TextStyle(
                                            fontSize: Consts.fontSizeSmall),
                                      ),
                                    ),
                                  ],
                                  onPressed: (int index) {
                                    if(this.mounted) setState(() {
                                      for (int i = 0;
                                          i < isSelectedLanguage.length;
                                          i++) {
                                        if (i == index) {
                                          isSelectedLanguage[i] = true;
                                        } else {
                                          isSelectedLanguage[i] = false;
                                        }
                                      }
                                      if (isSelectedLanguage[0]) {
                                        changeLanguage(context, 'en');
                                      } else if (isSelectedLanguage[1]) {
                                        changeLanguage(context, 'hi');
                                      }
                                    });
                                  },
                                  isSelected: isSelectedLanguage,
                                )
                              ]),
                          VerticalSpacing(10.0),
                        
                          Padding(
                              padding:
                                  const EdgeInsets.only(left: 8.0, right: 8.0),
                              child
                    : Row(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: <Widget>[
                          OutlineButton(
                            padding: EdgeInsets.all(5.0),
                            splashColor: Colors.blue[300],
                            borderSide:
                                BorderSide(color: ThemeColors.colorPrimary),
                            child: Text(' Retake Video ',
                                style: TextStyle(
                                    color: Colors.blue,
                                    fontSize: Consts.fontSizeMedium)),
                            onPressed: () {
                              _reset();
                            },
                          ),
                          HorizontalSpacing(10.0),
                          FlatButton(
                            padding: EdgeInsets.all(5.0),
                            color: ThemeColors.colorPrimary,
//            splashColor: Colors.greenAccent,
                            textColor: ThemeColors.white,

                            child: Text(' Play Recording ',
                                style:
                                    TextStyle(fontSize: Consts.fontSizeMedium)),
                            onPressed: () {
                              if(videoPath!=null) {
                                _navigateAndDisplaySelection(context);
                              } else{
                                //ConstantMethods.showToast("Technical exception with camera.");
                                ConstantMethods.showToast("There is some issue in recording video right now, please try again later.");
                                _onBackPressed();
                              }
                            },
                          ),
                        ],
                      ),
                VerticalSpacing(10.0),
              ],
            ));
          })),
    );
  }

  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;
    }
  }

  // Display 'Loading' text when the camera is still loading.
  Widget _cameraPreviewWidget(var size) {
    if (controller == null || !controller.value.isInitialized) {
      return const Text(
        'Loading',
        style: TextStyle(
          color: ThemeColors.white,
          fontSize: 20.0,
          fontWeight: FontWeight.w900,
        ),
      );
    }

    return Container(
      width: size,
      height: size,
      child: ClipRect(
        child: OverflowBox(
          alignment: Alignment.center,
          child: FittedBox(
            fit: BoxFit.fitWidth,
            child: Container(
              width: size,
              height: size / controller.value.aspectRatio,
//              height: size / controller.value.aspectRatio,
              child: CameraPreview(controller),

              // this is my CameraPreview
            ),
          ),
        ),
      ),
    );

    
  }

  /// Display a row of toggle to select the camera (or a message if no camera is available).
  Widget _cameraTogglesRowWidget() {
    if (cameras == null) {
      return Row();
    }

    CameraDescription selectedCamera =
        cameras[selectedCameraIdx]; 
    CameraLensDirection lensDirection =
        selectedCamera.lensDirection; 
    //CameraLensDirection lensDirection = CameraLensDirection.front;

    return Expanded(
      child: Align(
        alignment: Alignment.centerLeft,
        child: FlatButton.icon(
            onPressed: controller != null && controller.value.isRecordingVideo
                ? null
                : _onSwitchCamera,
            icon: Icon(_getCameraLensIcon(lensDirection)),
            label: Text(
                "${lensDirection.toString().substring(lensDirection.toString().indexOf('.') + 1)}")),
      ),
    );
  }

  /// Display the control bar with buttons to record videos.
  Widget _captureControlRowWidget() {
    return Expanded(
      child: Align(
        alignment: Alignment.center,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          mainAxisSize: MainAxisSize.max,
          children: <Widget>[
            IconButton(
              icon: const Icon(Icons.videocam, size: 40.0),
              color: ThemeColors.colorPrimary,
              onPressed: controller != null &&
                      controller.value.isInitialized &&
                      !controller.value.isRecordingVideo &&
                      videoPath == null
                  ? _onRecordButtonPressed
                  : null,
            ),
            IconButton(
              icon: const Icon(Icons.stop, size: 40.0),
              color: ThemeColors.red,
              onPressed: controller != null &&
                      controller.value.isInitialized &&
                      controller.value.isRecordingVideo &&
                      visibilityStop
                  ? _onStopButtonPressed
                  : null,
            ),
//                : new Container(),
          ],
        ),
      ),
    );
  }

  String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();

  Future<void> _onCameraSwitched(CameraDescription description) async {

      if (description == null) {
        return;
      }
      if(!(controller != null && controller.value.isRecordingVideo)){
        onNewCameraSelected(description);
      }

    
  }

  void onNewCameraSelected(CameraDescription cameraDescription) async {
    if (controller != null) {
      await controller.dispose();
    }

    controller = CameraController(
      cameraDescription, ResolutionPreset.low
    );

    //controller = cameraController;

    // If the controller is updated then update the UI.
    controller.addListener(() {
      if (mounted) setState(() {});
      if (controller.value.hasError) {
        Fluttertoast.showToast(
            msg: 'Camera error ${controller.value.errorDescription}',
            toastLength: Toast.LENGTH_SHORT,
            gravity: ToastGravity.CENTER,
            timeInSecForIosWeb: 1,
            backgroundColor: ThemeColors.red,
            textColor: ThemeColors.white);
      }
    });

    try {
      await controller.initialize();
    } on CameraException catch (e) {
      _showCameraException(e);
    }

    if (mounted) {
      setState(() {});
    }
  }

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

    _onCameraSwitched(selectedCamera);

    if (mounted) setState(() {
      selectedCameraIdx = selectedCameraIdx;
    });
  }

  void _onRecordButtonPressed() {
    _startVideoRecording().then((_) {
      /*if (mounted) setState(() {});*/
      //if (filePath != null) {
      final df = new DateFormat('dd-MM-yyyy hh:mm a');
      String date = df.format(new DateTime.now());

      if (mounted) setState(() {
//          selectedDate = date1.replaceAll("/", "-");
        selectedDate = date;
      });

      Fluttertoast.showToast(
          msg: 'Recording video started',
          toastLength: Toast.LENGTH_SHORT,
          gravity: ToastGravity.CENTER,
          timeInSecForIosWeb: 1,
          backgroundColor: ThemeColors.grey,
          textColor: ThemeColors.white);
      // }
    });
  }

  void _onStopButtonPressed() {
    if (cd.isRunning) {
      cd.cancel();
    }

    _stopVideoRecording().then((file) {
      if (mounted) setState(() {});
      if (file != null) {
        showInSnackBar('Video recorded to ${file.path}');
        videoPath = file;
      }

    });
  }

  Future<void> _startVideoRecording() async {
    //final CameraController? cameraController = controller;

    if (controller == null || !controller.value.isInitialized) {
      //showInSnackBar('Error: select a camera first.');
      Fluttertoast.showToast(
          msg: 'Please wait',
          toastLength: Toast.LENGTH_SHORT,
          gravity: ToastGravity.CENTER,
          timeInSecForIosWeb: 1,
          backgroundColor: ThemeColors.grey,
          textColor: ThemeColors.white);

      return;
    }

    if (controller.value.isRecordingVideo) {
      // A recording is already started, do nothing.
      return;
    }

    try {
      cd = CountdownTimer(
          new Duration(seconds: videoElapseTime), new Duration(seconds: 1));
//      var sub = cd.listen(null);
      var sub = cd.listen(null);
      sub.onData((duration) {
//        setState(() { _current = _start - duration.elapsed.inSeconds; });
        if (duration.elapsed.inSeconds == videoMinElapseTime) {
            // ((videoElapseTime - 5) < 20 ? 20 : (videoElapseTime - 5))) {
          _changed(true);
        }
        timer = duration.elapsed.inMinutes.toString().padLeft(2, '0') +
            ":" +
            duration.elapsed.inSeconds.toString().padLeft(2, '0');
        _changedTimer(timer + "/00:" + videoElapseTime.toString());
        print(timer);
      });

      sub.onDone(() {
        print("Done");
        _onStopButtonPressed();
        sub.cancel();
      });
      await controller.startVideoRecording();
    } on CameraException catch (e) {
      _showCameraException(e);
      return;
    }
  }




  Future<XFile> _stopVideoRecording() async {
  
    if (controller == null || !controller.value.isRecordingVideo) {
      return null;
    }

    try {
      _changedButtonVisibility(true);
      XFile video = await controller.stopVideoRecording();
      await GallerySaver.saveVideo(video.path); //for testing
      return video;
    } on CameraException catch (e) {
      _showCameraException(e);
      return null;
    }
   
  }

  void _showCameraException(CameraException e) {
    String errorText = 'Error: ${e.code}\nError Message: ${e.description}';
    print(errorText);
}
  
  Future<bool> _onBackPressed() {
    if (widget.isOpenSubsDashboard && (role == null || role.isEmpty)) {
      //subscriber
      return Navigator.pushAndRemoveUntil(
            context,
            MaterialPageRoute(builder: (context) => SubscriberDashboard()),
            (route) => false,
          ) ??
          false;
    } else {
      //Navigator.of(context).pop();
      return Future.value(Navigator.of(context).canPop());
    }
  }

 
0 Answers
Related