Flutter二维码扫描 2023-01-28 周六
简介
当前二维码扫描功能越来越普遍。在iOS原生,可以使用AVFoundation种的AVCapture相关的API进行自定义。
在Flutter层面,稍微想想就知道,Dart直接支持有点远,应该是需要通过插件的方式来实现。
插件选择
-
自己写插件,理论上是可以的。iOS的应该可以继续用AVCapture来做。安卓的应该也有对应的实现方案。
二维码扫描这个功能目前已经很普遍了,应该有三方插件可用。
自己造轮子,还是使用现有的轮子?大多数人的选择应该是不言而喻的。 -
进入Pub官网,输入scan关键字:
企业微信截图_94a7e1ab-2911-4470-bef5-c3895d1328f8.png
可以3个都看看,当然,也可以按照指标,直接选最多的那个qr_code_scanner。
如果不在意指标,很有可能会选第3个mobile_scanner。虽然没有提供例子图片,但是其介绍中提到其实现方式都是系统原生的;不像第1个,原生实现都是采用了不再更新的第三方库。
- 集成命令(这里是根据指标选的第1个):
flutter pub add qr_code_scanner
样例讨论
官网的Readme部分给出了一个例子,照抄可以用。这里给出一些不一样的思考。
热重载
// In order to get hot reload to work we need to pause the camera if the platform
// is android, or resume the camera if the platform is iOS.
@override
void reassemble() {
super.reassemble();
if (Platform.isAndroid) {
controller!.pauseCamera();
} else if (Platform.isIOS) {
controller!.resumeCamera();
}
}
-
这段代码特意加了注释,就是说想要flutter的热重载特性,就要重写State的reassemble方法,对相机进行复位。
-
考虑到扫码是一次性的动作,需要热重载吗?大多数的场景就是打开一个页面,对着二维码扫描,成功后退出扫描页面,返回扫描到的字符串。所以,热重载是个鸡肋,不要更好。
扫描视图
-
直接使用QRView就可以了,放在body中,默认就是全屏的。不需要放入Expanded中。
-
默认是没有浮层的。想要浮层,给overlay参数赋值QrScannerOverlayShape()就可以了。
浮层的样式,可以通过QrScannerOverlayShape()的参数调整。
扫描结果
- 这个有点绕:首先,通过onQRViewCreated参数,把内部的QRViewController引出来;然后QRViewController建立侦听,在侦听的回调函数中,把扫描结果包装成Barcode对象给出。一般扫描的结果取barcode.code就可以了。
void _onQRViewCreated(QRViewController controller) {
controller.scannedDataStream.listen((scanData) {
setState(() {
result = scanData;
});
});
}
- Demo中把QRViewController和Barcode保存起来,进行界面更新,其实没有必要。相机只有一个,初始化相机,关闭相机都是很费资源的操作。这个跟Flutter更新一次就重绘一次界面格格不入。所以,扫描界面还是做成stateless的无状态widget比较好。setState很昂贵,最好不要用。
资源回收
QRViewController由QRView创建,通过onQRViewCreated暴露出来,但是dispose方法需要使用者来调用。
权限管理
-
QRView通过onPermissionSet参数来进行权限管理。比如模拟器就没有摄像头,onPermissionSet会通过回调的bool类型参数设置为false来告诉使用者。
-
最简单的处理方法就是直接退出扫描界面,然后toast提示一下就好了。
扫描结果去重
- QRViewController侦听函数会返回多个扫描结果。下面的例子代码setState会执行多次,导致不可预料的结果。
controller.scannedDataStream.listen((scanData) {
setState(() {
result = scanData;
});
});
-
最直接能想到的就是用一个bool变量控制,只执行一次。
-
另外一种方法,就是QRViewController的pauseCamera方法。取到一次结果之后,就停止扫描,这样能防止多重结果带来的不确定性。
参考代码
/// 页面结构
Widget _buildBody() {
return QRView(
key: GlobalKey(debugLabel: 'QR'),
overlay: QrScannerOverlayShape(),
onQRViewCreated: (qrViewController) {
qrViewController.scannedDataStream.listen((barcode) {
String code = barcode.code ?? '';
if (code.isNotEmpty) {
qrViewController.pauseCamera();
EasyLoading.showInfo(code, duration: const Duration(seconds: 5));
qrViewController.dispose();
Get.back(result: code);
}
});
},
onPermissionSet: (qrViewController, isPermission) {
if (!isPermission) {
EasyLoading.showInfo('no permission');
qrViewController.dispose();
Get.back();
}
},
);
}
其他功能
相机相关的功能都包含在QRViewController中,比如火炬灯光,切换前后摄像头等。