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

Flutter切换摄像头与地图视图时崩溃,报surface_texture_external_texture致命错误

解决Flutter摄像头与地图切换崩溃问题

问题根源

崩溃日志中的Check failed: state_ == AttachmentState::kAttached是底层SurfaceTexture状态不匹配导致的,核心原因:

  • 切换视图时未正确同步CameraController的生命周期:代码中toggleCameraPreview方法未被实际调用,直接通过setState切换状态,导致Controller的销毁/初始化逻辑失效,出现已销毁的Controller被CameraPreview引用的情况。
  • 异步初始化与Widget渲染不同步,导致CameraPreview尝试使用未就绪或已释放的摄像头资源。

修复步骤

1. 修正视图切换触发逻辑

将按钮的点击事件替换为调用生命周期管理方法,而不是直接修改状态:

InkWell(
  onTap: toggleCameraPreview, // 替换原有的直接setState逻辑
  child: Padding(
    // ... 原有布局代码不变
  ),
)

2. 优化CameraController生命周期管理

修改toggleCameraPreview和initCameraController方法,确保Controller的创建与销毁和视图状态同步:

void toggleCameraPreview() async {
  // 先更新视图状态,再处理Controller资源
  setState(() {
    camera_preview_major = !camera_preview_major;
  });

  if (camera_preview_major) {
    // 切换到摄像头视图时,仅在Controller未初始化时创建
    if (!camera_controller.value.isInitialized) {
      await initCameraController();
    }
  } else {
    // 延迟销毁,确保Widget树更新完成后再释放资源
    await Future.delayed(const Duration(milliseconds: 100));
    if (camera_controller.value.isInitialized) {
      await camera_controller.dispose();
    }
  }
}

Future<void> initCameraController() async {
  // 避免重复初始化
  if (camera_controller.value.isInitialized) {
    return;
  }
  
  camera_controller = CameraController(
    config.cameras[0], 
    ResolutionPreset.low,
    enableAudio: false
  );
  
  try {
    await camera_controller.initialize();
    if (mounted) {
      setState(() {});
    }
  } on CameraException catch (e) {
    switch (e.code) {
      case 'CameraAccessDenied':
        // 可在此添加权限提示逻辑
        break;
      default:
        print('摄像头初始化错误: $e');
        break;
    }
  }
}

3. 完善CameraPreview有效性检查

修改getCameraPreview方法,避免引用无效的Controller:

Widget getCameraPreview(CameraController controller, Size size) {
  if (controller == null || !controller.value.isInitialized) {
    return const Center(
      child: LinearProgressIndicator(color: Colors.grey),
    );
  }
  return SizedBox(width: size.width, child: CameraPreview(controller));
}

4. 优化资源释放逻辑

确保页面销毁时所有资源都被正确释放:

@override
void dispose() async {
  // 释放摄像头控制器
  if (camera_controller.value.isInitialized) {
    await camera_controller.dispose();
  }
  // 释放地图控制器
  final mapController = await _controller.future;
  mapController.dispose();
  
  super.dispose();
}

额外注意事项

  • 确保config.cameras已正确获取设备摄像头列表,避免空指针异常
  • 在初始化摄像头前,可增加权限二次检查,确保摄像头权限已授予
  • 切换时的延迟是为了等待Widget树更新完成,避免CameraPreview在Controller释放前仍处于活跃状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 12:16:28