Flutter组件内条码相机显示及扫描结果保存实现方案咨询
Flutter 持续条码扫描实现(账单录入场景)
一、推荐的条码扫描组件
- mobile_scanner:Flutter官方维护的插件,支持实时持续扫描,性能稳定,兼容多种条码格式,适配iOS/Android,是账单录入这类持续扫描场景的首选。
- barcode_scan2:老牌扫描插件,功能完善,但维护频率低于mobile_scanner,适合需要兼容旧版本项目的场景。
- flutter_barcode_scanner:轻量易用,但默认是单次扫描逻辑,需额外配置才能实现持续扫描,适合简单需求场景。
二、持续扫描核心实现(以mobile_scanner为例)
1. 依赖与权限配置
在pubspec.yaml中添加依赖:
dependencies: mobile_scanner: ^5.0.0
- Android:在
android/app/src/main/AndroidManifest.xml中添加相机权限:
<uses-permission android:name="android.permission.CAMERA" />
- iOS:在
ios/Runner/Info.plist中添加相机权限描述:
<key>NSCameraUsageDescription</key> <string>需要相机权限进行条码扫描</string>
2. 持续扫描代码实现
实现相机持续开启+扫描记录展示的页面,自动过滤重复扫描结果:
import 'package:flutter/material.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:shared_preferences/shared_preferences.dart'; class BarcodeScannerPage extends StatefulWidget { const BarcodeScannerPage({super.key}); @override State<BarcodeScannerPage> createState() => _BarcodeScannerPageState(); } class _BarcodeScannerPageState extends State<BarcodeScannerPage> { MobileScannerController controller = MobileScannerController(); List<String> scannedCodes = []; String? lastScannedCode; final Duration debounceDuration = const Duration(seconds: 2); @override void initState() { super.initState(); _loadSavedCodes(); } // 加载本地已保存的扫描记录 Future<void> _loadSavedCodes() async { final prefs = await SharedPreferences.getInstance(); setState(() { scannedCodes = prefs.getStringList('scanned_codes') ?? []; }); } // 保存新扫描的条码到本地 Future<void> _saveScannedCode(String code) async { if (code == lastScannedCode) return; setState(() { scannedCodes.add(code); lastScannedCode = code; }); final prefs = await SharedPreferences.getInstance(); await prefs.setStringList('scanned_codes', scannedCodes); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('已记录条码:$code')), ); Future.delayed(debounceDuration, () { setState(() { lastScannedCode = null; }); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('账单条码录入')), body: Column( children: [ // 相机预览区域 Expanded( flex: 3, child: MobileScanner( controller: controller, onDetect: (capture) { final barcodes = capture.barcodes; for (final barcode in barcodes) { final code = barcode.rawValue; if (code != null) _saveScannedCode(code); } }, ), ), // 扫描记录列表 Expanded( flex: 2, child: ListView.builder( itemCount: scannedCodes.length, itemBuilder: (context, index) { return ListTile( title: Text(scannedCodes[index]), trailing: IconButton( icon: const Icon(Icons.delete), onPressed: () => _deleteCode(index), ), ); }, ), ), ], ), ); } // 删除单条扫描记录 Future<void> _deleteCode(int index) async { setState(() { scannedCodes.removeAt(index); }); final prefs = await SharedPreferences.getInstance(); await prefs.setStringList('scanned_codes', scannedCodes); } @override void dispose() { controller.dispose(); super.dispose(); } }
三、扫描结果保存方案
1. 轻量本地存储:SharedPreferences
适合存储少量扫描记录,操作简单,快速实现基础需求,如示例所示。
2. 本地数据库:SQLite(推荐大量记录场景)
如果需要存储条码关联的额外信息(如扫描时间、账单备注),可使用sqflite插件建立数据表管理记录。
3. 云端存储:Firebase Firestore/后端API
需要多设备同步或后台管理时,可将扫描结果上传至云端数据库或后端接口。
关键注意事项
- 防抖处理:必须添加防抖逻辑,避免同一条码被重复记录。
- 权限处理:首次启动需申请相机权限,可配合
permission_handler插件完成权限申请流程。 - 资源释放:页面销毁时及时释放相机控制器,避免资源泄漏。
内容的提问来源于stack exchange,提问作者Yous y
相关产品推荐
相关产品推荐

