Flutter Stepper中实现多二维码扫描的问题求助
解决Stepper中多MobileScanner回调冲突的方案
问题核心在于Stepper组件默认会渲染所有步骤的内容(即使非当前步骤只是隐藏),导致两个MobileScanner实例同时处于活跃状态,所以扫描时两个onDetect都会触发。下面给出两种直接可行的解决思路:
方案一:只渲染当前步骤的MobileScanner
根据Stepper的activeStep状态,动态控制对应步骤的扫描器是否渲染,非当前步骤用空组件占位。这样同一时间只有一个扫描器在运行,从根源避免回调冲突。
示例代码:
int _activeStep = 0; @override Widget build(BuildContext context) { return Stepper( currentStep: _activeStep, onStepContinue: () { if (_activeStep < 1) { setState(() => _activeStep += 1); } }, steps: [ Step( title: const Text('扫描代金券'), content: _activeStep == 0 ? MobileScanner( onDetect: (capture) async { final barcodes = capture.barcodes; if (barcodes.isEmpty) return; final voucherCode = barcodes.first.rawValue!; // 弹出验证进度弹窗 showDialog( context: context, barrierDismissible: false, builder: (_) => const Center(child: CircularProgressIndicator()), ); // 执行司机权限验证逻辑 bool isValid = await _validateDriverPermission(voucherCode); Navigator.pop(context); if (isValid) { setState(() => _activeStep += 1); } }, ) : const SizedBox.shrink(), ), Step( title: const Text('扫描车辆'), content: _activeStep == 1 ? MobileScanner( onDetect: (capture) async { final barcodes = capture.barcodes; if (barcodes.isEmpty) return; final carCode = barcodes.first.rawValue!; // 弹出验证进度弹窗 showDialog( context: context, barrierDismissible: false, builder: (_) => const Center(child: CircularProgressIndicator()), ); // 执行车辆匹配验证逻辑 bool isMatch = await _validateCarMatch(carCode); Navigator.pop(context); if (isMatch) { // 验证完成后的后续逻辑,比如提交订单 } }, ) : const SizedBox.shrink(), ), ], ); } // 模拟验证接口 Future<bool> _validateDriverPermission(String code) async { await Future.delayed(const Duration(seconds: 2)); return true; } Future<bool> _validateCarMatch(String code) async { await Future.delayed(const Duration(seconds: 2)); return true; }
方案二:用控制器控制扫描器启停
如果需要保留所有步骤的组件状态(比如不想重新初始化扫描器),可以给两个扫描器分别绑定独立的MobileScannerController,在切换步骤时主动启停对应控制器。
示例代码:
int _activeStep = 0; final MobileScannerController _voucherController = MobileScannerController(); final MobileScannerController _carController = MobileScannerController(); @override void initState() { super.initState(); // 初始启动代金券扫描器 _voucherController.start(); } @override void dispose() { _voucherController.dispose(); _carController.dispose(); super.dispose(); } void _switchStep(int newStep) { setState(() => _activeStep = newStep); // 切换时启停对应控制器 if (newStep == 0) { _voucherController.start(); _carController.stop(); } else { _carController.start(); _voucherController.stop(); } } @override Widget build(BuildContext context) { return Stepper( currentStep: _activeStep, onStepContinue: () => _switchStep(_activeStep + 1), onStepCancel: () => _switchStep(_activeStep - 1), steps: [ Step( title: const Text('扫描代金券'), content: MobileScanner( controller: _voucherController, onDetect: (capture) async { // 代金券扫描逻辑同方案一 }, ), ), Step( title: const Text('扫描车辆'), content: MobileScanner( controller: _carController, onDetect: (capture) async { // 车辆扫描逻辑同方案一 }, ), ), ], ); }
两种方案对比
- 方案一更轻量,非当前步骤的扫描器完全不渲染,节省资源;
- 方案二适合需要保留扫描器状态的场景(比如不想每次切换步骤都重新初始化相机)。
内容的提问来源于stack exchange,提问作者MRS
相关产品推荐
相关产品推荐

