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 生成二维码

Dart 复制代码
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、全屏扫描页面

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

Dart 复制代码
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

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

Dart 复制代码
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 中实现二维码扫描功能,并能够应用到你的项目中。

相关推荐
江上清风山间明月10 小时前
Flutter开发的应用页面非常多时如何高效管理路由
android·flutter·路由·页面管理·routes·ongenerateroute
Zsnoin能20 小时前
flutter国际化、主题配置、视频播放器UI、扫码功能、水波纹问题
flutter
早起的年轻人21 小时前
Flutter CupertinoNavigationBar iOS 风格导航栏的组件
flutter·ios
HappyAcmen21 小时前
关于Flutter前端面试题及其答案解析
前端·flutter
coooliang1 天前
Flutter 中的单例模式
javascript·flutter·单例模式
coooliang1 天前
Flutter项目中设置安卓启动页
android·flutter
JIngles1231 天前
flutter将utf-8编码的字节序列转换为中英文字符串
java·javascript·flutter
B.-1 天前
在 Flutter 中实现文件读写
开发语言·学习·flutter·android studio·xcode
freflying11192 天前
使用jenkins构建Android+Flutter项目依赖自动升级带来兼容性问题及Jenkins构建速度慢问题解决
android·flutter·jenkins
机器瓦力2 天前
Flutter应用开发:对象存储管理图片
flutter