Flutter扫码功能完美实现

   日期:2021-04-16     浏览:173    评论:0    
核心提示:如何快速完美地实现Flutter扫码效果预览笔者非常执着于能使用直观的效果展示,但是因为CSDN的大小限制,笔者做了很强的压缩,大致还是能看出代码的最终结果实现需求实现需求是否实现自定义扫码区域大小✅连续扫码✅从相册选择✅支持二维码✅支持条形码✅扫描动画特效✅环境Flutter版本 1.22.0.stablepubspec.yaml中添加依赖scan: 0.0.7images_picker: 0.0.8其中sca

如何快速完美地实现Flutter扫码

效果预览

笔者非常执着于能使用直观的效果展示,但是因为CSDN的大小限制,笔者做了很强的压缩,大致还是能看出代码的最终结果

实现需求

实现需求 是否实现
自定义扫码区域大小(可全屏)
连续扫码
从相册选择
支持二维码
支持条形码
扫描动画特效

环境

Flutter版本 1.22.0.stable

pubspec.yaml中添加依赖

scan: 0.0.7
images_picker: 0.0.8

其中scan插件用于扫码,images_picker插件用于从相册选择二维码或者条形码图片

代码实现

前置条件

既然是扫码,肯定会访问到设备的摄像头,所以需要申请到相机的权限
至于权限申请的部分可以参考我的另一篇博文 Flutter Android权限问题

Dart代码部分

import 'package:flutter/material.dart';
import 'package:images_picker/images_picker.dart';
import 'package:scan/scan.dart';

/// 扫码页面
class QRScannerPage extends StatefulWidget {
  final QRScannerPageConfig config;

  const QRScannerPage({this.config});

  @override
  _QRScannerPageState createState() => _QRScannerPageState();
}

class _QRScannerPageState extends State<QRScannerPage> {
  StateSetter stateSetter;

  IconData lightIcon = Icons.flash_on;

  ScanController controller = ScanController();

  List<String> result = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('扫码'),
        ),
        body: _buildBody());
  }

  Widget _buildBody() {
    return Stack(children: [
      ScanView(
        controller: controller,
        scanAreaScale: widget.config.scanAreaSize,
        scanLineColor: widget.config.scanLineColor,
        onCapture: (String data) async {
          await showResult(content: '扫码结果\t$data');
          controller.resume();
        },
      ),
      Positioned(
        left: 50,
        bottom: 100,
        child: StatefulBuilder(
          builder: (BuildContext context, StateSetter setState) {
            stateSetter = setState;
            return MaterialButton(
                child: Icon(lightIcon, size: 30, color: Colors.greenAccent),
                onPressed: () {
                  controller.toggleTorchMode();
                  if (lightIcon == Icons.flash_on) {
                    lightIcon = Icons.flash_off;
                  } else {
                    lightIcon = Icons.flash_on;
                  }
                  stateSetter(() {});
                });
          },
        ),
      ),
      Positioned(
        right: 50,
        bottom: 100,
        child: MaterialButton(
            child: Icon(Icons.image,
                size: 30, color: Color.fromRGBO(4, 184, 67, 1)),
            onPressed: () async {
              await pickImage();
              // DialogUtil.showCommonDialog(context, '$result');
            }),
      ),
    ]);
  }

  Future<void> showResult({String content}) async {
    return showGeneralDialog(
        context: context,
        pageBuilder: (context, anim1, anim2) {},
        barrierColor: Colors.black.withOpacity(.6),
        barrierDismissible: true,
        barrierLabel: "",
        transitionDuration: Duration(milliseconds: 150),
        transitionBuilder: (context, anim1, anim2, child) {
          return Transform.scale(
              scale: anim1.value,
              child: Opacity(
                  opacity: anim1.value,
                  child: Center(
                    child: Padding(
                        padding: const EdgeInsets.all(12.0),
                        child: new Material(
                          type: MaterialType.transparency,
                          child: new Container(
                              height: 450,
                              width: 300,
                              decoration: ShapeDecoration(
                                  color: Colors.white,
                                  shape: RoundedRectangleBorder(
                                      borderRadius: BorderRadius.all(
                                    Radius.circular(8.0),
                                  ))),
                              child: Column(
                                children: [
                                  Expanded(
                                    flex: 2,
                                    child: Align(
                                      alignment: Alignment.center,
                                      child: Text(
                                        content,
                                        style:
                                            TextStyle(height: 1, fontSize: 18),
                                      ),
                                    ),
                                  ),
                                  DividerHorizontal(),
                                  Expanded(
                                    flex: 1,
                                    child: Row(
                                      children: [
                                        Expanded(
                                            child: GestureDetector(
                                          behavior: HitTestBehavior.opaque,
                                          onTap: () {
                                            Navigator.pop(context);
                                          },
                                          child: Align(
                                            alignment: Alignment.center,
                                            child: Text(
                                              '确认',
                                              style: TextStyle(
                                                  color: Color(0xFFFF7B85),
                                                  fontSize: 18),
                                            ),
                                          ),
                                        ))
                                      ],
                                    ),
                                  )
                                ],
                              )),
                        )),
                  )));
        });
  }

  Future pickImage() async {
    List<Media> files = await ImagesPicker.pick(
      pickType: PickType.image,
      count: 9,
    );
    if (files != null && files.isNotEmpty) {
      for (int i = 0; i < files.length; i++) {
        String value = await Scan.parse(files[i].path);
        result.add(value);
      }
      showResult(content: result.toString());
    }
  }
}

class QRScannerPageConfig {
  double scanAreaSize;
  Color scanLineColor;

  QRScannerPageConfig(
      {this.scanAreaSize: 1.0,
      this.scanLineColor: const Color.fromRGBO(4, 184, 67, 1)});
}

class DividerHorizontal extends StatelessWidget {
  final double height;
  final Color color;

  DividerHorizontal({this.height: 1, this.color: const Color(0xFFF8F9F8)});

  @override
  Widget build(BuildContext context) {
    return Container(
      height: height,
      color: color,
    );
  }
}

这个代码都是可以直接copy使用的,如果有需要可以适当地更改

项目的配置

建议Android的最低兼容要在API 21(Android 5.0)以上,这个是 images_picker这个插件所要求的

权限配置

在AndroidManifest.xml文件配置相应权限,但是注意的一点:这个只是安装时的权限申请,在Android 7.0之后是需要运行时权限,所以这个问题的解决需要参考我的这篇文章 Flutter Android权限问题
其实说到底还是动态权限的问题。

总结

笔者在此之前,试用了多个扫码的插件,效果要么就是无法全屏,要么就是无法出现扫码的效果,亦或者是无法连续扫码(每次扫码结束都会从扫码界面回退),总体来说这个scan插件算是比较好地满足了我的需求,后续我会专门写一篇文章来讲解:如何开发一个可供Dart调用的Android的扫码插件。

最近笔者在公司使用Flutter技术独自开发一款企业级的物联网应用,如果对笔者感兴趣,欢迎关注笔者。读者有好的建议,也欢迎在下面留言。码字不易,请给

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
更多>相关资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服