Flutter 画笔绘制二维码扫描框


一、CustomPaint介绍

1. CustomPaint

CustomPaint 是一个 Widget 类,提供了绘制时所需的画布。

  const CustomPaint({
    Key? key,
    // CustomPainter  绘制背景
    this.painter,
    // CustomPainter  绘制前景
    this.foregroundPainter,
    // 尺寸,默认为0,如果有子widget则为子widget的大小
    this.size = Size.zero,
    this.isComplex = false,
    this.willChange = false,
    Widget? child,
  })

2. CustomPainter

CustomPainter 一个用于实现CustomPaint绘制的接口,需要实现此接口来进行自定义绘制。

class _ScanFramePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // 绘图的主要实现
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // 返回true 则会重新绘制,执行 paint函数,返回false 则不会重新绘制
    return true;
  }

}

3. Paint & Canvas

Paint 画笔
Canvas 画布
两者组合可绘制各种图形。

4. 示例(绘制文本背景)

1. 自定义 CustomPainter

class TextPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()..color = Colors.blue;
    // size是widget的尺寸,即CustomPaint的尺寸
    canvas.drawRect(Rect.fromLTRB(0, 0, size.width, size.height), paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

2. 显示CustomPaint以及Text

class CustomTextWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: CustomPaint(
          painter: TextPainter(),
          child: Text('Flutter 画笔绘制二维码扫描框'),
        ),
      ),
    );
  }
}

3. 最终效果
在这里插入图片描述

二、计算扫描框四个点坐标

先看效果图,然后根据 xy轴以及扫描框的大小计算坐标。

在这里插入图片描述
坐标计算代码如下,看注释。

class _ScanFramePainter extends CustomPainter {
  //默认定义扫描框为 260边长的正方形
  final Size frameSize = Size.square(260.0);

  @override
  void paint(Canvas canvas, Size size) {
    // 按扫描框居中来计算,全屏尺寸与扫描框尺寸的差集 除以 2 就是扫描框的位置
    Offset diff = size - frameSize;
    double leftTopX = diff.dx / 2;
    double leftTopY = diff.dy / 2;
    //根据左上角的坐标和扫描框的大小可得知扫描框矩形
    var rect =
        Rect.fromLTWH(leftTopX, leftTopY, frameSize.width, frameSize.height);
    // 4个点的坐标
    Offset leftTop = rect.topLeft;
    Offset leftBottom = rect.bottomLeft;
    Offset rightTop = rect.topRight;
    Offset rightBottom = rect.bottomRight;

    //定义画笔
    Paint paint = Paint()
      ..color = Colors.blue  //颜色
      ..strokeWidth = 4.0   //画笔线条宽度
      ..style = PaintingStyle.stroke; // 画笔的模式,填充还是只绘制边框
    
    //绘制正方形
    canvas.drawRect(rect, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

调整 CustomPaint 的尺寸后查看效果

  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomPaint(
        painter: _ScanFramePainter(),
        child: Container(),//全屏
      ),
    );
  }

绘制方框只是为了查看扫描框位置是否正确

在这里插入图片描述

三、绘制透明灰扫描背景

除扫描框以外,分成四个矩形组成在一起就是扫描框背景,在paint函数中绘制扫描背景

    //绘制罩层
    //画笔
    Paint paint = Paint()
      ..color = Color(0x40cccccc) //透明灰
      ..style = PaintingStyle.fill; // 画笔的模式,填充
    //左侧矩形
    canvas.drawRect(Rect.fromLTRB(0, 0, leftTopX, size.height), paint);
    //右侧矩形
    canvas.drawRect(
      Rect.fromLTRB(rightTop.dx, 0, size.width, size.height),
      paint,
    );
    //中上矩形
    canvas.drawRect(Rect.fromLTRB(leftTopX, 0, rightTop.dx, leftTopY), paint);
    //中下矩形
    canvas.drawRect(
      Rect.fromLTRB(leftBottom.dx, leftBottom.dy, rightBottom.dx, size.height),
      paint,
    );

四、绘制扫描框

定义边角的长度,和扫描线的移动高度

  final double cornerLength = 20.0;

在paint函数中继续绘制扫描框

  // 重新设置画笔
    paint
      ..color = Colors.blue
      ..strokeWidth = 4.0
      ..strokeCap = StrokeCap.square  // 解决因为线宽导致交界处不是直角的问题
      ..style = PaintingStyle.stroke;

    // 横向线条的坐标偏移
    Offset horizontalOffset = Offset(cornerLength, 0);
    // 纵向线条的坐标偏移
    Offset verticalOffset = Offset(0, cornerLength);
    // 左上角
    canvas.drawLine(leftTop, leftTop + horizontalOffset, paint);
    canvas.drawLine(leftTop, leftTop + verticalOffset, paint);
    // 左下角
    canvas.drawLine(leftBottom, leftBottom + horizontalOffset, paint);
    canvas.drawLine(leftBottom, leftBottom - verticalOffset, paint);
    // 右上角
    canvas.drawLine(rightTop, rightTop - horizontalOffset, paint);
    canvas.drawLine(rightTop, rightTop + verticalOffset, paint);
    // 右下角
    canvas.drawLine(rightBottom, rightBottom - horizontalOffset, paint);
    canvas.drawLine(rightBottom, rightBottom - verticalOffset, paint);

此时查看效果
在这里插入图片描述

五、绘制扫描线并添加动画

1. 绘制扫描线

首先要确定扫描线的Y坐标,由外部传值

  _ScanFramePainter({this.lineMoveValue}) : assert(lineMoveValue != null);

  // 百分比值,0 ~ 1,然后计算Y坐标
  final double lineMoveValue;

在paint函数中继续绘制扫描线

    //修改画笔线条宽度
    paint.strokeWidth = 2;
    // 扫描线的移动值
    var lineY = leftTopY + frameSize.height * lineMoveValue;
    // 10 为线条与方框之间的间距,绘制扫描线
    canvas.drawLine(
      Offset(leftTopX + 10.0, lineY),
      Offset(rightTop.dx - 10.0, lineY),
      paint,
    );

2. 使扫描线移动

使用 Animation 实现线条移动

class _ScanFrameState extends State<ScanFrame> with TickerProviderStateMixin {
  Animation<double> _animation;
  AnimationController _controller;

  //起始之间的线性插值器 从 0.05 到 0.95 百分比。
  final Tween<double> _rotationTween = Tween(begin: 0.05, end: 0.95);

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this,     //实现 TickerProviderStateMixin
      duration: Duration(seconds: 3), //动画时间 3s
    );

    _animation = _rotationTween.animate(_controller)
      ..addListener(() => setState(() {}))
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          _controller.repeat();
        } else if (status == AnimationStatus.dismissed) {
          _controller.forward();
        }
      });

    _controller.repeat();
  }

  @override
  void dispose() {
    // 释放动画资源
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: _ScanFramePainter(lineMoveValue: _animation.value),
      child: Container(),
    );
  }
}

六、Flutter 二维码扫描效果图

在这里插入图片描述

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
要在 Flutter 应用程序中实现二维码扫描,可以使用 `qr_code_scanner` 或 `barcode_scan` 插件。 `qr_code_scanner` 插件提供了一个 `QRView` widget,可以轻松地将二维码扫描器集成到应用程序中。以下是一个简单的示例: 1. 添加 `qr_code_scanner` 插件到 `pubspec.yaml` 文件中: ``` dependencies: qr_code_scanner: ^0.4.3 ``` 2. 导入 `qr_code_scanner` 包: ``` import 'package:qr_code_scanner/qr_code_scanner.dart'; ``` 3. 创建一个 `QRViewController` 和一个 `QRView`: ``` QRViewController controller; final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); @override Widget build(BuildContext context) { return QRView( key: qrKey, onQRViewCreated: _onQRViewCreated, ); } void _onQRViewCreated(QRViewController controller) { this.controller = controller; controller.scannedDataStream.listen((scanData) { // 处理扫描到的二维码 }); } ``` 当用户扫描二维码时,`QRViewController` 会不断发送扫描数据到 `scannedDataStream`,可以在监听函数中处理这些数据。 如果想要更多的控制权,可以使用 `barcode_scan` 插件。这个插件提供了一个 `scan()` 方法,可以启动一个新的扫描器界面,并且可以自定义扫描器的外观和行为。以下是一个简单的示例: 1. 添加 `barcode_scan` 插件到 `pubspec.yaml` 文件中: ``` dependencies: barcode_scan: ^2.0.0 ``` 2. 导入 `barcode_scan` 包: ``` import 'package:barcode_scan/barcode_scan.dart'; ``` 3. 调用 `scan()` 方法并处理返回的数据: ``` Future<void> scan() async { String barcode = await BarcodeScanner.scan(); // 处理扫描到的二维码 } ``` 在调用 `scan()` 方法时,会启动一个新的扫描器界面,并等待用户扫描二维码。当用户扫描二维码后,会返回扫描到的数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值