You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中CameraController初始化失败:空值检查运算符报错

问题:Bloc模式下相机预览触发Null check错误

我使用Bloc模式构建相机访问功能,打开相机预览页面时出现错误:"Null check operator used on a null value",空值指向CameraController。我认为问题出在CameraController未在调用前完成初始化。

工具类CameraUtils代码

class CameraUtils {
  Future<CameraController> getCameraController(
      ResolutionPreset resolutionPreset) async {
    final cameras = await availableCameras();
    final camera = cameras.first;

    return CameraController(
      camera,
      resolutionPreset,
      imageFormatGroup: ImageFormatGroup.bgra8888,
    );
  }

  Future<String> getPath() async => join(
        (await getTemporaryDirectory()).path,
        '${DateTime.now()}.png',
      );
}

CameraBloc类代码

class CameraBloc extends Bloc<CameraEvent, CameraState> {
  late final CameraUtils _cameraUtils;
  late final ResolutionPreset _resolutionPreset;
  late final CameraLensDirection _cameraLensDirection;

  CameraController? _controller;

  late File pictureTaken;

  CameraBloc() : super(CameraInitial()) {
    _cameraUtils = CameraUtils();
    _resolutionPreset = ResolutionPreset.high;
    _cameraLensDirection = CameraLensDirection.back;
    on<CameraInitialized>(_onCameraInitialized);
    on<CameraCaptured>(_onCameraCaptured);
    on<CameraStopped>(_onCameraStopped);
    add(CameraInitialized());
  }

  Stream<CameraState> _onCameraInitialized(
      CameraInitialized event, emit) async* {
    try {
      _controller = await _cameraUtils.getCameraController(_resolutionPreset);
      await _controller!.initialize();
      yield CameraReady();
    } on CameraException catch (error) {
      _controller!.dispose();
      yield CameraFailure(error: error.description ?? "Camera generic error");
    } catch (error) {
      yield CameraFailure(error: error.toString());
    }
  }

  Stream<CameraState> _onCameraCaptured(CameraCaptured event, emit) async* {
    if (state is CameraReady) {
      yield CameraCaptureInProgress();
      try {
        final path = await _cameraUtils.getPath();
        await _controller?.takePicture();
        yield CameraCaptureSuccess(path: path);
      } on CameraException catch (error) {
        yield CameraCaptureFailure(
            error: error.description ?? "Generic camera capture failure");
      }
    }
  }

  Stream<CameraState> _onCameraStopped(CameraStopped event, emit) async* {
    _controller?.dispose();
    yield CameraInitial();
  }

  void initController() async {
    _controller = await _cameraUtils.getCameraController(_resolutionPreset);
    await _controller!.initialize();
  }

  CameraController getController() => _controller!;

  void getCamera() => add(CameraInitialized());
}

CameraView代码

class CameraView extends StatefulWidget {
  const CameraView({Key? key}) : super(key: key);

  @override
  State<CameraView> createState() => _CameraViewState();
}
class _CameraViewState extends State<CameraView> {
  @override
  Widget build(BuildContext context) {
    return CameraPreview(BlocProvider.of<CameraBloc>(context).getController());
}
}

解决方案

1. 基于Bloc状态安全渲染相机预览

修改CameraView,通过BlocBuilder监听Bloc状态,仅当状态为CameraReady时渲染CameraPreview,其他状态显示加载或错误界面:

class CameraView extends StatefulWidget {
  const CameraView({Key? key}) : super(key: key);

  @override
  State<CameraView> createState() => _CameraViewState();
}

class _CameraViewState extends State<CameraView> {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<CameraBloc, CameraState>(
      builder: (context, state) {
        if (state is CameraReady) {
          return CameraPreview(context.read<CameraBloc>().getController());
        } else if (state is CameraInitial || state is CameraCaptureInProgress) {
          return const Center(child: CircularProgressIndicator());
        } else if (state is CameraFailure || state is CameraCaptureFailure) {
          return Center(child: Text((state as dynamic).error));
        }
        return const SizedBox.shrink();
      },
    );
  }
}

2. 修复Bloc中的空安全隐患

  • 在_onCameraInitialized的异常捕获中,将_controller!.dispose()改为_controller?.dispose(),避免_controller为null时触发空检查:
Stream<CameraState> _onCameraInitialized(
    CameraInitialized event, emit) async* {
  try {
    _controller = await _cameraUtils.getCameraController(_resolutionPreset);
    await _controller!.initialize();
    yield CameraReady();
  } on CameraException catch (error) {
    _controller?.dispose(); // 改为可选链调用
    yield CameraFailure(error: error.description ?? "Camera generic error");
  } catch (error) {
    _controller?.dispose(); // 新增:确保异常时释放资源
    yield CameraFailure(error: error.toString());
  }
}
  • 可以修改getController()方法,增加状态判断或返回可空类型,避免强制解包:
CameraController? getController() => _controller;
// 或者在状态为CameraReady时才返回
CameraController? getController() {
  if (state is CameraReady) {
    return _controller;
  }
  return null;
}

3. 确保Bloc初始化逻辑的异步顺序

Bloc构造函数中添加的CameraInitialized事件是异步处理的,页面加载时Bloc可能仍处于CameraInitial状态,此时_controller尚未初始化。通过BlocBuilder监听状态变化,能确保只有在CameraReady状态下才访问CameraController,从根源避免空值问题。

内容的提问来源于stack exchange,提问作者Finley Adams

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 11:36:14