当前位置: 首页 > news >正文

Flutter 扫描二维码

在今天的移动开发中,二维码扫描已经成为了常见的功能之一。Flutter作为一款跨平台的开发框架,提供了丰富的插件和功能,使得开发者可以轻松实现二维码扫描以及图像识别功能。本文将介绍如何在Flutter中通过结合scan插件、permission_handler插件以及image_picker插件,实现二维码扫描和从相册选择二维码图片的功能。

效果图:

1、相机扫描二维码

2、相册选择二维码并扫描

1、项目依赖

首先,我们需要在pubspec.yaml文件中添加以下依赖:

dependencies: flutter: sdk: flutter scan: ^1.6.0 # 用于扫描二维码 permission_handler: ^10.2.0 # 用于权限请求 image_picker: ^1.0.7 # 用于从相册选择图片

这些插件的作用如下:

  • scan:提供二维码扫描功能。
  • permission_handler:用于请求相机权限,确保用户授权后才能使用相机进行二维码扫描。
  • image_picker:允许从相册选择图片,便于用户上传二维码图片进行识别。

2、配置权限

在 Android 平台上,为了使用相机功能,需要在AndroidManifest.xml文件中添加必要的权限:

<uses-permission android:name="android.permission.CAMERA"/>

在 IOS平台上修改Info.plist文件,打开ios/Runner/Info.plist文件,确保添加以下配置(iOS用虚拟机是调试不了的,实测打不开相机):

<key>NSCameraUsageDescription</key> <string>需要访问相机用于二维码扫描</string> <key>NSPhotoLibraryUsageDescription</key> <string>需要访问相册选择图片</string>

3、实现二维码扫描功能

接下来,我们来看看如何实现二维码扫描功能。我们需要创建一个主界面,在其中添加按钮来请求相机权限,并进行二维码扫描。

没有二维码的小伙伴可以查看上一篇文章:Flutter 生成二维码

import 'package:flutter/material.dart'; import 'package:scan/scan.dart'; // 导入 scan 插件 import 'package:permission_handler/permission_handler.dart'; // 导入权限请求插件 import 'package:image_picker/image_picker.dart'; // 导入相册选择插件 void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: QRScannerScreen(), ); } } class QRScannerScreen extends StatefulWidget { @override _QRScannerScreenState createState() => _QRScannerScreenState(); } class _QRScannerScreenState extends State<QRScannerScreen> { ScanController controller = ScanController(); // 创建扫描控制器 String qrcode = 'Unknown'; // 默认二维码内容 // 执行二维码扫描前请求权限 _requestPermissions() async { var cameraStatus = await Permission.camera.request(); if (cameraStatus.isGranted) { // 权限已授予,开始扫描 print("相机权限已授予,开始扫描"); // 点击按钮后跳转到全屏扫描界面 Navigator.push( context, MaterialPageRoute(builder: (context) => QRScanPage(controller: controller)), ).then((result) { controller.pause(); // 暂停扫描 // 扫描完成后获取返回的二维码数据并更新显示 if (result != null) { setState(() { qrcode = result; // 更新二维码内容 }); } }); } else { // 权限被拒绝或未授权 print("相机权限未授予,请授权"); // 可以引导用户去设置页面 } } // 选择相册中的二维码图片 _selectImageFromGallery() async { final picker = ImagePicker(); final pickedFile = await picker.pickImage(source: ImageSource.gallery); if (pickedFile != null) { String result = await Scan.parse(pickedFile.path) ?? ''; // 解析选中的图片 setState(() { qrcode = result; // 更新二维码内容 }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('二维码扫描器'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ // 显示二维码扫描结果 const Text( '二维码扫描结果:', style: TextStyle(fontSize: 18), ), const SizedBox(height: 10), Text( qrcode, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold), ), const SizedBox(height: 30), // 扫描按钮 ElevatedButton( onPressed: _requestPermissions, // 执行权限请求 child: const Text('请求权限并开始扫描'), ), const SizedBox(height: 30), // 选择相册按钮 ElevatedButton( onPressed: _selectImageFromGallery, // 选择相册 child: const Text('从相册选择二维码图片'), ), ], ), ), ); } }

代码解释

  1. 权限请求:我们通过permission_handler插件请求相机权限。用户允许后才能进行二维码扫描。
  2. 扫描功能:我们使用ScanController来控制扫描过程。点击按钮后,跳转到全屏扫描页面QRScanPage,在该页面,用户可以通过相机扫描二维码。
  3. 从相册选择图片:我们利用image_picker插件允许用户从相册选择图片,并解析二维码。

4、全屏扫描页面

当用户点击扫描按钮时,应用会进入一个全屏扫描页面。在该页面中,二维码扫描功能全屏展示,并且添加了一个“选择相册”按钮,让用户可以在扫描时直接选择图片。

class QRScanPage extends StatelessWidget { final ScanController controller; const QRScanPage({super.key, required this.controller}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('扫描二维码')), body: Stack( alignment: Alignment.center, children: [ // 使用 ScanView 进行全屏扫描 ScanView( controller: controller, scanAreaScale: 0.8, // 设置扫描区域占满整个屏幕 scanLineColor: Colors.green.shade400, // 设置扫描线颜色 onCapture: (data) { // 扫描到二维码后,返回数据 controller.pause(); // 暂停扫描 Navigator.pop(context, data); // 返回扫描结果 }, ), // 在屏幕上添加选择相册按钮 Positioned( bottom: 100, child: ElevatedButton( onPressed: () async { // 选择相册 final picker = ImagePicker(); final pickedFile = await picker.pickImage(source: ImageSource.gallery); if (pickedFile != null) { String result = await Scan.parse(pickedFile.path) ?? ''; // 解析选中的图片 Navigator.pop(context, result); // 返回二维码结果 } }, child: const Text( '选择相册', style: TextStyle( fontSize: 18, color: Colors.white, fontWeight: FontWeight.bold, ), ), ), ), ], ), ); } }

代码解释

  • ScanView:提供扫描界面,支持自定义扫描区域和扫描线颜色。
  • onCapture:当扫描到二维码时,会返回扫描结果并暂停扫描。
  • 选择相册按钮:点击按钮可以让用户从相册选择二维码图片进行解析。

完整demo

可以直接复制到新项目跑起来

import 'package:flutter/material.dart'; import 'package:scan/scan.dart'; // 导入 scan 插件 import 'package:permission_handler/permission_handler.dart'; // 导入权限请求插件 import 'package:image_picker/image_picker.dart'; // 导入相册选择插件 void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: QRScannerScreen(), ); } } class QRScannerScreen extends StatefulWidget { @override _QRScannerScreenState createState() => _QRScannerScreenState(); } class _QRScannerScreenState extends State<QRScannerScreen> { ScanController controller = ScanController(); // 创建扫描控制器 String qrcode = 'Unknown'; // 默认二维码内容 // 执行二维码扫描前请求权限 _requestPermissions() async { var cameraStatus = await Permission.camera.request(); if (cameraStatus.isGranted) { // 权限已授予,开始扫描 print("相机权限已授予,开始扫描"); // 点击按钮后跳转到全屏扫描界面 Navigator.push( context, MaterialPageRoute(builder: (context) => QRScanPage(controller: controller)), ).then((result) { controller.pause(); // 暂停扫描 // 扫描完成后获取返回的二维码数据并更新显示 if (result != null) { setState(() { qrcode = result; // 更新二维码内容 }); } }); } else { // 权限被拒绝或未授权 print("相机权限未授予,请授权"); // 可以引导用户去设置页面 } } // 选择相册中的二维码图片 _selectImageFromGallery() async { final picker = ImagePicker(); final pickedFile = await picker.pickImage(source: ImageSource.gallery); if (pickedFile != null) { String result = await Scan.parse(pickedFile.path) ?? ''; // 解析选中的图片 setState(() { qrcode = result; // 更新二维码内容 }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('二维码扫描器'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ // 显示二维码扫描结果 const Text( '二维码扫描结果:', style: TextStyle(fontSize: 18), ), const SizedBox(height: 10), Text( qrcode, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold), ), const SizedBox(height: 30), // 扫描按钮 ElevatedButton( onPressed: _requestPermissions, // 执行权限请求 child: const Text('请求权限并开始扫描'), ), const SizedBox(height: 30), // 选择相册按钮 ElevatedButton( onPressed: _selectImageFromGallery, // 选择相册 child: const Text('从相册选择二维码图片'), ), ], ), ), ); } } class QRScanPage extends StatelessWidget { final ScanController controller; const QRScanPage({super.key, required this.controller}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('扫描二维码')), body: Stack( alignment: Alignment.center, children: [ // 使用 ScanView 进行全屏扫描 ScanView( controller: controller, scanAreaScale: 0.8, // 设置扫描区域占满整个屏幕 scanLineColor: Colors.green.shade400, // 设置扫描线颜色 onCapture: (data) { // 扫描到二维码后,返回数据 controller.pause(); // 暂停扫描 Navigator.pop(context, data); // 返回扫描结果 }, ), // 在屏幕上添加选择相册按钮 Positioned( bottom: 100, child: ElevatedButton( onPressed: () async { // 选择相册 final picker = ImagePicker(); final pickedFile = await picker.pickImage(source: ImageSource.gallery); if (pickedFile != null) { String result = await Scan.parse(pickedFile.path) ?? ''; // 解析选中的图片 Navigator.pop(context, result); // 返回二维码结果 } }, child: const Text( '选择相册', style: TextStyle( fontSize: 18, color: Colors.white, fontWeight: FontWeight.bold, ), ), ), ), ], ), ); } }

总结

本文展示了如何在 Flutter 中实现二维码扫描和从相册选择二维码图片的功能。通过使用scanpermission_handlerimage_picker插件,我们可以轻松地添加二维码扫描和图片识别功能。在开发实际应用时,可能还需要处理更多细节,例如处理不同平台的权限请求、优化扫描体验等。

希望这篇文章能帮助你更好地理解如何在 Flutter 中实现二维码扫描功能,并能够应用到你的项目中。

http://www.jsqmd.com/news/1295273/

相关文章:

  • 深度解析:基于内存补丁技术的微信QQ防撤回完整指南
  • 终极GTA5防崩溃菜单:YimMenu完整使用教程与安全防护指南
  • 终极指南:快速掌握TileMapDual插件的7个高效技巧
  • 上海普高转轨申请美本,托福如何稳出分?|上海美本托福选课攻略 - 品牌品鉴馆
  • 2026年优选薛城区管道疏通服务相关公司推荐指南 - 起跑123
  • 寻找廊坊评价高的木屑颗粒生产厂家怎么选更省心 - 品牌优推
  • 秒级处置身份劫持威胁:XDR 自动化威胁响应技术架构与实战效能研究
  • HExHTTP入门教程:5分钟学会检测Web缓存投毒漏洞
  • 海口翡翠回收哪家正规?2026本地靠谱变现指南|全城实体门店极速回款 - 全国二奢机构参考
  • PDF批量处理效率倍增术:3个场景化应用让你告别重复劳动
  • 【MySQL】 主从复制多拓扑搭建实验
  • git Update project 报错
  • Windows用户福利:Fridare实现原生deb包处理,告别Linux虚拟机
  • 【设计模式】观察者模式
  • python-nameparser性能优化:处理大规模姓名数据的最佳实践
  • 武汉学技术选哪个学校好?武汉新华电脑学校全专业招生简章解读 - 武汉中职最新信息发布
  • 如何将普通小爱音箱升级为AI智能助手:MiGPT完整指南
  • 终极Evernote笔记备份指南:3步掌握数据自主权
  • MC开服教程-白嫖面板服务器!小白也可以!
  • OpenWork多用户协作功能:团队共享工作区与权限管理全解析
  • 2026年7月高新技术企业代理商哪家可靠综合各维度评测 - 甄选测评馆
  • 紧急修复!AI配音项目交付前最后30分钟停顿微调清单(含Audacity+Whisper联合校验模板)
  • 3天掌握Qlib:AI量化投资平台完整部署与实战指南
  • 智慧校园系统适配数据安全法:核心合规要点梳理
  • 贵阳卖金新规落地!黄金回收“五查五避”准则,变现必备自查清单 - 日常比对手册
  • Redis数据库密码修改的两种方法
  • 越华环保集团:数字化污水治理的“L3突围”与落地逻辑
  • 制造运营系统MOM与PLM、ERP、CAPP系统的协同关系
  • 2026写论文崩溃瞬间[特殊字符]90%学生都踩的坑,一个工具全解救
  • 拆解上海顶奢回收猫腻!爱马仕瑕疵乱判定乱象,收的顶专业团队公正核验 - 奢侈品回收评测