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
相关产品推荐
相关产品推荐

