如何在Flutter容器中嵌入条形码/二维码扫描器?
实现内嵌式条码扫描器(Flutter Web/移动端)
你之前使用的flutter_barcode_scanner、simple_barcode_scanner等库都是封装好的全屏扫描页面,无法直接嵌入自定义容器。要实现类似移动购物APP的内嵌式扫描效果,推荐使用mobile_scanner库——它支持将扫描视图作为普通Widget嵌入任意布局容器,同时兼容Flutter Web和移动端。
步骤1:添加依赖
在pubspec.yaml中引入最新稳定版依赖:
dependencies: mobile_scanner: ^5.0.0
步骤2:替换为内嵌式扫描组件
以下是修改后的代码,实现了在指定容器内显示扫描视图,同时保留你需要的边框、圆角等样式:
import 'package:flutter/material.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; class Scanner extends StatefulWidget { final double width; final double height; final int borderRadius; final int borderWidth; final Color borderColor; const Scanner({ super.key, required this.width, required this.height, required this.borderRadius, required this.borderWidth, required this.borderColor, }); @override _ScannerState createState() => _ScannerState(); } class _ScannerState extends State<Scanner> { String _result = ''; late MobileScannerController _controller; bool _isScanning = true; @override void initState() { super.initState(); _controller = MobileScannerController( detectionSpeed: DetectionSpeed.normal, facing: CameraFacing.back, torchEnabled: false, ); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Column( children: [ // 内嵌扫描容器 Container( width: widget.width, height: widget.height, decoration: BoxDecoration( borderRadius: BorderRadius.circular(widget.borderRadius.toDouble()), border: Border.all( color: widget.borderColor, width: widget.borderWidth.toDouble(), ), overflow: Clip.antiAlias, // 确保扫描视图跟随圆角裁剪 ), child: Stack( children: [ // 核心扫描视图 MobileScanner( controller: _controller, onDetect: (capture) { final List<Barcode> barcodes = capture.barcodes; if (barcodes.isNotEmpty && _isScanning) { setState(() { _result = barcodes.first.rawValue ?? '未识别'; _isScanning = false; // 停止扫描避免重复识别 }); // 可选:延迟后恢复扫描 Future.delayed(const Duration(seconds: 2), () { setState(() => _isScanning = true); }); } }, ), // 添加扫描框遮罩(模拟APP扫描样式) CustomPaint( painter: ScannerOverlayPainter( borderColor: widget.borderColor, borderWidth: widget.borderWidth, borderRadius: widget.borderRadius, ), ), ], ), ), const SizedBox(height: 20), // 扫描结果展示 Text('扫描结果:$_result', style: const TextStyle(fontSize: 16)), // 手动控制扫描按钮(可选) ElevatedButton( onPressed: () { setState(() { _isScanning = !_isScanning; }); }, child: Text(_isScanning ? '暂停扫描' : '继续扫描'), ), ], ); } } // 自定义扫描框遮罩 painter class ScannerOverlayPainter extends CustomPainter { final Color borderColor; final int borderWidth; final int borderRadius; ScannerOverlayPainter({ required this.borderColor, required this.borderWidth, required this.borderRadius, }); @override void paint(Canvas canvas, Size size) { final scanAreaWidth = size.width * 0.7; final scanAreaHeight = size.height * 0.7; final left = (size.width - scanAreaWidth) / 2; final top = (size.height - scanAreaHeight) / 2; // 绘制半透明遮罩 final paint = Paint() ..color = Colors.black.withOpacity(0.5) ..style = PaintingStyle.fill; canvas.drawRect(Offset.zero & size, paint); // 裁剪扫描区域 final path = Path() ..addRRect(RRect.fromRectAndRadius( Rect.fromLTWH(left, top, scanAreaWidth, scanAreaHeight), Radius.circular(borderRadius.toDouble()), )) ..addRect(Offset.zero & size) ..fillType = PathFillType.evenOdd; canvas.drawPath(path, paint); // 绘制扫描框边框 final borderPaint = Paint() ..color = borderColor ..style = PaintingStyle.stroke ..strokeWidth = borderWidth.toDouble(); canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromLTWH(left, top, scanAreaWidth, scanAreaHeight), Radius.circular(borderRadius.toDouble()), ), borderPaint, ); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; }
关键说明
- 内嵌核心:
MobileScanner是一个普通Widget,可以直接放入Container等布局组件中,无需跳转新页面。 - 扫描遮罩:通过
CustomPaint绘制半透明遮罩和扫描框,模拟主流APP的扫描视觉效果。 - 权限处理:Flutter Web中会自动请求摄像头权限;移动端需在
AndroidManifest.xml/Info.plist中添加摄像头权限配置。 - 扫描控制:通过
_isScanning变量控制是否处理扫描结果,避免重复识别。
替代方案
如果mobile_scanner不符合需求,还可以尝试:
barcode_scan2:支持自定义扫描界面,需自行搭建布局;- 基于
camera库+mlkit_barcode_scanning:手动实现摄像头预览+条码识别,灵活性最高,但代码量更大。
内容的提问来源于stack exchange,提问作者Faris A
相关产品推荐
相关产品推荐

