Flutter使用qr_code_scanner仅iOS端出现StateError (Bad state: No element)错误
问题根因分析
这个问题仅在iOS端触发的核心原因是你错误地把热重载阶段的相机控制逻辑用到了扫码成功回调中:
reassemble方法里的Android暂停相机、iOS恢复相机逻辑是为了适配热重载场景,本身没有问题- 但你在扫码成功的
scannedDataStream监听回调里沿用了同样的逻辑,导致iOS端扫码成功后相机没有停止工作,会持续识别到同一个二维码,多次触发回调。第一次回调已经执行Navigator.pop销毁了扫码页面,第二次回调再用已经失效的context执行pop操作,就会触发异常。
Android端不会出现这个问题,是因为扫码成功后直接暂停了相机,只会触发一次回调,页面销毁后不会有后续逻辑执行。
解决步骤
- 修正扫码成功后的相机控制逻辑:无论Android还是iOS,扫码成功获取到有效数据后第一时间暂停相机,避免重复触发回调
- 执行
Navigator.pop前先判断当前页面是否处于挂载状态,避免操作已销毁的context - 仅在需要更新UI时调用
setState,不要把无关的业务逻辑都包裹在setState回调里 - 对二维码内容的解码逻辑增加异常捕获,避免非预期的二维码格式导致程序崩溃
修正后的核心代码示例
void onQRViewCreated(QRViewController qrViewController) { this.qrViewController = qrViewController; qrViewController.scannedDataStream.listen((event) async { // 扫码成功第一时间暂停相机,全平台通用 await qrViewController.pauseCamera(); String? rawData = event.code; if (rawData == null || rawData.isEmpty) { // 空数据直接恢复相机继续扫描 await qrViewController.resumeCamera(); return; } String placer = ""; try { Uri data = Uri.dataFromString(rawData); String para1 = data.queryParameters["buy"] ?? ""; if (para1.isNotEmpty) { Codec<String, String> stringToBase64 = utf8.fuse(base64); placer = stringToBase64.decode(para1); } } catch (e) { // 格式不符合时提示用户,恢复相机继续扫描 if (mounted) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('二维码格式无效'))); } await qrViewController.resumeCamera(); return; } // 执行pop前判断页面是否仍挂载 if (mounted) { WidgetsBinding.instance.addPostFrameCallback((_) { Navigator.pop(context, placer); }); } }); }
同时建议调整销毁逻辑,增加非空判断避免控制器未初始化时报错:
@override void dispose() { qrViewController.dispose(); super.dispose(); }
内容的提问来源于stack exchange,提问作者H Wong
相关产品推荐
相关产品推荐

