使用相机包授予权限会在出现异常时暂停调试器

boo*_*boy 3 camera dart flutter flutter-dependencies flutter-packages

我使用相机包来实现简单的功能。我主要遵循包提供的示例。当我打开相机小组件页面时,程序包会自动提示提供相机和麦克风的权限。单击允许这两种权限后,调试器将暂停,但出现异常:

Exception has occurred.
FlutterError (A CameraController was used after being disposed.
Once you have called dispose() on a CameraController, it can no longer be used.).
Run Code Online (Sandbox Code Playgroud)

这是所需的代码:

class CameraPage extends StatefulWidget {
  @override
  _CameraPageState createState() => _CameraPageState();
}

class _CameraPageState extends State<CameraPage>
    with WidgetsBindingObserver {
  CameraController _controller;
  List<CameraDescription> _availableCameras;
  ...

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    _initialize();
  }

  Future<void> _initialize() async {
    await _getCameras();
    _controller = CameraController(_availableCameras[0], ResolutionPreset.high);
    await _controller.initialize();
    if (!mounted) {
      return;
    }
    setState(() {});
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.inactive) {
      _controller?.dispose();
    } else if (state == AppLifecycleState.resumed) {
      if (_controller != null) {
        _setCurrentCamera(_controller.description);
      }
    }
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    _controller.dispose();
    super.dispose();
  }


  Future<List<CameraDescription>> _getCameras() async {
    List<CameraDescription> camDescriptions;
      camDescriptions = await availableCameras();

      _availableCameras = camDescriptions;
    return camDescriptions;
  }

  @override
  Widget build(BuildContext context) {
    ...
  }


  Future<void> _setCurrentCamera(CameraDescription cameraDescription) async {
    if (_controller != null) {
      await _controller.dispose();
    }
    _controller = CameraController(
      cameraDescription,
      ResolutionPreset.high,
      enableAudio: false,
    );

    // If the _controller is updated then update the UI.
    _controller.addListener(() {
      if (mounted) setState(() {});

      if (_controller.value.hasError) {
        print('Camera error ${_controller.value.errorDescription}');
      }
    });

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

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

  void _switchCamera() {
    if (_controller != null && !_controller.value.isRecordingVideo) {
      CameraLensDirection direction = _controller.description.lensDirection;
      CameraLensDirection required = direction == CameraLensDirection.front
          ? CameraLensDirection.back
          : CameraLensDirection.front;
      for (CameraDescription cameraDescription in _availableCameras) {
        if (cameraDescription.lensDirection == required) {
          _setCurrentCamera(cameraDescription);
          return;
        }
      }
    }
  }


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

}
Run Code Online (Sandbox Code Playgroud)

调试器在这里指出异常:

  Future<void> _initialize() async {
    await _getCameras();
    _controller = CameraController(_availableCameras[0], ResolutionPreset.high);
    //-------------HERE------------------
    await _controller.initialize();
    if (!mounted) {
      return;
    }
    setState(() {});
  }
Run Code Online (Sandbox Code Playgroud)

一旦我恢复调试器并尝试再次打开此相机页面,就不再出现错误/异常。仅在第一次接受权限后才会发生。

Hem*_*Raj 6

也许真正的罪魁祸首是didChangeAppLifecycleState

一旦您调用await _controller.initialize();并显示权限对话框,AppLifecycleState.inactive就会触发生命周期事件,并根据您在 中的代码处理当前控制器didChangeAppLifecycleState,因此当应用程序在授予权限后恢复并尝试继续时,它会抛出错误。

尝试删除

if (state == AppLifecycleState.inactive) {
  _controller?.dispose();
}
Run Code Online (Sandbox Code Playgroud)

或者有一个局部变量来检查是否正在初始化并在初始化时忽略处理,例如

Future<void> _initialize() async {
  await _getCameras();
  _controller = CameraController(_availableCameras[0], ResolutionPreset.high);
  _initializing = true;
  await _controller.initialize();
  _initializing = false;
  if (!mounted) {
    return;
  }
  setState(() {});
}
Run Code Online (Sandbox Code Playgroud)

并在didChangeAppLifecycleState

if (state == AppLifecycleState.inactive && !_initializing) {
  _controller?.dispose();
}
Run Code Online (Sandbox Code Playgroud)

编辑:

可能是,我想我发现了这个问题,实际的问题正如didChangeAppLifecycleState预期的那样,如果结果属实,则正在处理中的if条款,如果不是,则只是处理任何活动控制器。因此,当您调用初始化并等待权限时,在权限 future 解析之前,正在由 处置。didChangeAppLifecycleState_controller_setCurrentCamera_controllerdidChangeAppLifecycleState

我的解决方案只需进行简单的更改即可工作。将您的更改initState

@override
void initState() {
  super.initState();
  _initializing = true; // set to true
  WidgetsBinding.instance.addObserver(this);
  _initialize();
}
Run Code Online (Sandbox Code Playgroud)

初始化后更改您的_initialize函数,_initializing = false例如,

Future<void> _initialize() async {
  await _getCameras();
  _controller = CameraController(_availableCameras[0],ResolutionPreset.high);
  await _controller.initialize();
  _initializing = false; // set to false
  if (!mounted) {
    return;
  }
  setState(() {});
}
Run Code Online (Sandbox Code Playgroud)

和你didChangeAppLifecycleState

@override
void didChangeAppLifecycleState(AppLifecycleState state) {
  if(_initializing){
    return;
  }
  if (state == AppLifecycleState.inactive) {
    _controller?.dispose();
  } else if (state == AppLifecycleState.resumed) {
    if (_controller != null) {
      _setCurrentCamera(_controller.description);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这样,如果_initializing == true您从未处置当前控制器。


希望有帮助!