Flutter中间镂空的二维码扫描控件

1、UI效果图:

2、中间镂空UI:

复制代码
class CenterTransparentMask extends CustomClipper<Path> {
  final double? width;

  CenterTransparentMask({this.width});

  @override
  Path getClip(Size size) {
    final path = Path()
      ..addRect(Rect.fromLTWH(0, 0, size.width,
          size.height + MediaQuery.of(Get.context!).padding.bottom))
      ..addRect(Rect.fromLTWH(
          (size.width - (width ?? 200)) / 2,
          (size.height +
                  MediaQuery.of(Get.context!).padding.bottom -
                  (width ?? 200)) /
              2,
          width ?? 200,
          width ?? 200));

    return path..fillType = PathFillType.evenOdd; 
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) {
    return false;
  }
}
复制代码
3、扫码UI:
复制代码
class ScanWidget extends StatefulWidget {
  const ScanWidget({super.key});

  @override
  State<ScanWidget> createState() => _ScanWidgetState();
}

class _ScanWidgetState extends State<ScanWidget> with TickerProviderStateMixin {
  late Animation<double> animation;
  late AnimationController _controller;

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

  void initAnima() {
    _controller = AnimationController(
      duration: const Duration(seconds: 4),
      vsync: this,
    );

    animation = Tween(begin: -100.0, end: 100.0).animate(_controller)
      ..addListener(() {
        if (mounted) setState(() => {});
      });
    _controller.repeat();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.center,
      children: [
        _scanBorder(),
        _scanCenter(context),
        Transform.translate(
          offset: Offset(0, animation.value),
          child: _scanLine(),
        ),
      ],
    );
  }

  Widget _scanCenter(BuildContext context) {
    return ClipPath(
      clipper: CenterTransparentMask(), 
      child: BackdropFilter(
        filter: ImageFilter.blur(sigmaX: 2, sigmaY: 2),
        child: Container(
          color: Colors.black.withOpacity(0.5),
          width: SystemUtil().getScreenWidth(context),
          height: SystemUtil().getScreenHeight(context),
        ),
      ),
    );
  }

  Widget _scanBorder() {
    return Image.asset(
      ImageUtils.getImgPath("img_border", "scan"),
      width: 200,
      height: 200,
    );
  }

  Widget _scanLine() {
    return Image.asset(
      ImageUtils.getImgPath("image_line", "scan"),
      width: 200,
      height: 6,
    );
  }
}

3、图片加载工具类:

复制代码
class ImageUtils {
  static String getImgPath(String name, String moduleName,
      {String format = "png"}) {
    return "assets/images/$moduleName/$name.$format";
  }
}
相关推荐
骄马之死4 小时前
SpringMVC + SpringBoot 核心知识点总结
java·spring boot·后端
sbjdhjd4 小时前
Redis 主从复制、哨兵高可用与 Cluster 集群部署实验手册
运维·前端·redis·云原生·开源·bootstrap·html
乐兮创想 小林5 小时前
企业官网移动端性能优化实战:从 Core Web Vitals 到图片/CDN/响应式的工程清单
前端·性能优化·网站建设·北京网站建设公司
前端一小卒5 小时前
不手写代码的第 30 天,我才明白前端这个岗位还剩什么
前端·javascript·ai编程
Ajie'Blog5 小时前
Copilot Agent Tasks API 开放:AI 编程开始进入后台任务时代
服务器·前端·javascript·人工智能·copilot·ai编程
郑洁文6 小时前
基于Spring Boot的流浪动物救助网站
java·spring boot·后端·毕设·流浪动物救助
老毛肚6 小时前
jeecgboot vue TS & 模板化 04
前端·javascript·vue.js
螺丝钉code7 小时前
JAVA项目 Claude code CLAUDE.md 到底应该怎么写
java·人工智能·claude code
AI_零食7 小时前
鸿蒙PC Electron跨平台应用开发:24时区时间表应用详解
前端·华为·electron·开源·harmonyos·鸿蒙
摇滚侠8 小时前
Maven 入门+高深 单一架构案例 54-59
java·架构·maven·intellij-idea