Flutter二维码扫描插件code_scanner的使用

发布于 1周前 作者 songsunli 来自 Flutter

Flutter二维码扫描插件code_scanner的使用

code_scanner 是一个用于Flutter的二维码扫描插件,支持通过摄像头扫描二维码以及从相册中选择图片读取二维码。该插件会响应相机和相册的权限请求,并提供扫描和读取功能。

平台支持

  • iOS: 支持iOS 8.0及以上版本
  • Android: 最低支持API 23

配置

iOS配置

info.plist 文件中添加以下内容:

<key>NSCameraUsageDescription</key>
<string>需要访问您的相机以进行二维码扫描</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问您的相册以读取二维码图片</string>
<key>io.flutter.embedded_views_preview</key>
<true/>
Android配置

AndroidManifest.xml 文件中添加以下内容:

<manifest ... xmlns:tools="http://schemas.android.com/tools">
    <uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
</manifest>

依赖库

使用方法

View & View Controller

CodeScanner 是用于显示二维码扫描界面的Widget,CodeScannerController 是用于控制扫描行为的控制器。

/// 扫描Widget
CodeScanner(
  controller: controller,
  isScanFrame: true, // 是否显示扫描框,默认为true
  scanFrameSize: Size(200, 200), // 扫描框大小,默认为200x200
  frameWidth: 8, // 扫描框边框宽度,默认为8
  frameColor: Color(0xcc222222), // 扫描框颜色,默认为深灰色
)

/// Widget控制器
controller = CodeScannerController();
获取扫描数据

通过监听 scanDataStream 流来获取扫描到的二维码数据。

/// 监听扫描数据
controller.scanDataStream.listen((data) {
  print('扫描到的数据: $data');
});
获取读取数据

如果从相册中读取二维码图片成功,isSuccessReadDataStream 流会返回 true;如果失败,则返回 false。你也可以直接监听 readDataStream 流来获取读取结果,成功时返回实际数据,失败时返回 null

/// 监听读取成功的标志
controller.isSuccessReadDataStream.listen((success) {
  if (success) {
    print('读取成功');
  } else {
    print('读取失败');
  }
});

/// 监听读取数据
controller.readDataStream.listen((data) {
  if (data != null) {
    print('读取到的数据: $data');
  } else {
    print('读取失败');
  }
});
方法
  • 开始扫描await controller.startScan();(当 CodeScanner 创建时会自动调用此方法)
  • 停止扫描await controller.stopScan();(默认在控制器销毁时调用)
  • 从相册读取二维码await controller.readDataFromGallery();
  • 打开闪光灯await controller.lightON();
  • 关闭闪光灯await controller.lightOFF();
  • 切换闪光灯await controller.toggleLight();

完整示例代码

以下是一个完整的示例代码,展示了如何使用 code_scanner 插件进行二维码扫描和从相册读取二维码。

import 'package:code_scanner/code_scanner.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(home: CodeScannerHome()));
}

class CodeScannerHome extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('QR Scanner Example'),
      ),
      body: Center(
        child: FloatingActionButton(
          onPressed: () {
            Navigator.of(context).push(
              MaterialPageRoute(builder: (context) => CodeScannerExample()),
            );
          },
          child: Icon(Icons.camera_alt),
        ),
      ),
    );
  }
}

class CodeScannerExample extends StatefulWidget {
  [@override](/user/override)
  _CodeScannerExampleState createState() => _CodeScannerExampleState();
}

class _CodeScannerExampleState extends State<CodeScannerExample> {
  CodeScannerController controller;

  [@override](/user/override)
  void initState() {
    super.initState();
    this.controller = CodeScannerController();
  }

  [@override](/user/override)
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('QR Scanner'),
        leading: IconButton(
          key: Key('closeButton'),
          icon: const Icon(Icons.close),
          onPressed: () {
            Navigator.pop(context, '');
          },
        ),
      ),
      body: Stack(
        alignment: AlignmentDirectional.center,
        children: [
          // 扫描区域
          CodeScanner(
            controller: controller,
            isScanFrame: true,
            frameColor: Color(0xcc222222),
          ),
          
          // 提示文本
          Container(
            margin: const EdgeInsets.only(bottom: 500),
            padding: const EdgeInsets.all(5.0),
            width: 300,
            decoration: BoxDecoration(
              color: Color(0xcc222222),
              border: Border.all(color: Color(0xcc222222)),
              borderRadius: BorderRadius.circular(10),
            ),
            child: Text(
              'Scan code',
              style: TextStyle(color: Colors.white, fontSize: 17),
              textAlign: TextAlign.center,
            ),
          ),
          
          // 显示扫描结果
          Container(
            margin: const EdgeInsets.only(top: 350),
            padding: const EdgeInsets.all(5.0),
            width: 300,
            decoration: BoxDecoration(
              color: Color(0xcc222222),
              border: Border.all(color: Color(0xcc222222)),
              borderRadius: BorderRadius.circular(10),
            ),
            child: StreamBuilder<String>(
              stream: controller.scanDataStream,
              builder: (context, snapshot) {
                return Text(
                  'Scan Data: ${snapshot.data}',
                  style: TextStyle(color: Colors.white, fontSize: 17),
                  textAlign: TextAlign.center,
                );
              },
            ),
          ),
          
          // 显示读取结果
          Container(
            margin: const EdgeInsets.only(top: 450),
            padding: const EdgeInsets.all(5.0),
            width: 300,
            decoration: BoxDecoration(
              color: Color(0xcc222222),
              border: Border.all(color: Color(0xcc222222)),
              borderRadius: BorderRadius.circular(10),
            ),
            child: StreamBuilder<String>(
                stream: controller.readDataStream,
                builder: (context, snapshot) {
                  return (snapshot.hasData)
                      ? Text(
                          'Read Data: ${snapshot.data}',
                          style: TextStyle(color: Colors.white, fontSize: 17),
                          textAlign: TextAlign.center,
                        )
                      : Text(
                          'Read Failure',
                          style: TextStyle(color: Colors.white, fontSize: 17),
                          textAlign: TextAlign.center,
                        );
                }),
          ),
          
          // 操作按钮
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              Container(
                margin: const EdgeInsets.only(top: 600),
                child: FloatingActionButton(
                  heroTag: "hero1",
                  child: Icon(Icons.lightbulb_outline),
                  backgroundColor: Color(0xcc222222),
                  onPressed: () async {
                    await controller.toggleLight();
                  },
                ),
              ),
              Container(
                margin: const EdgeInsets.only(top: 600),
                child: FloatingActionButton(
                  heroTag: "hero2",
                  child: Icon(Icons.photo_library),
                  backgroundColor: Color(0xcc222222),
                  onPressed: () async {
                    await controller.readDataFromGallery();
                  },
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

更多关于Flutter二维码扫描插件code_scanner的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter二维码扫描插件code_scanner的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter应用中使用code_scanner插件进行二维码扫描的示例代码。这个示例将展示如何集成该插件、请求必要的权限以及实现二维码扫描功能。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加code_scanner依赖:

dependencies:
  flutter:
    sdk: flutter
  code_scanner: ^4.0.0  # 请检查最新版本号并替换

然后运行flutter pub get来安装依赖。

2. 请求权限

在Android和iOS上扫描二维码通常需要相机权限。因此,你需要在AndroidManifest.xmlInfo.plist中声明这些权限。

Android (AndroidManifest.xml)

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="true" />

iOS (Info.plist)

<key>NSCameraUsageDescription</key>
<string>需要访问相机以扫描二维码</string>

3. 实现二维码扫描功能

接下来,在你的Flutter代码中实现二维码扫描功能。以下是一个完整的示例:

import 'package:flutter/material.dart';
import 'package:code_scanner/code_scanner.dart';
import 'package:permission_handler/permission_handler.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ScannerPage(),
    );
  }
}

class ScannerPage extends StatefulWidget {
  @override
  _ScannerPageState createState() => _ScannerPageState();
}

class _ScannerPageState extends State<ScannerPage> {
  final CodeScannerController _controller = CodeScannerController();
  String? result;
  bool isPermissionGranted = false;

  @override
  void initState() {
    super.initState();
    _controller.addListener(() {
      setState(() {
        result = _controller.value.text;
      });
    });

    _requestPermission();
  }

  Future<void> _requestPermission() async {
    var status = await Permission.camera.status;
    if (!status.isGranted) {
      var result = await Permission.camera.request();
      if (result.isGranted) {
        setState(() {
          isPermissionGranted = true;
        });
        _controller.start();
      }
    } else {
      setState(() {
        isPermissionGranted = true;
      });
      _controller.start();
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('二维码扫描'),
      ),
      body: Center(
        child: isPermissionGranted
            ? Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Expanded(
                    flex: 5,
                    child: AspectRatio(
                      aspectRatio: _controller.value.aspectRatio,
                      child: CodeScannerViewBuilder(
                        builder: (BuildContext context, CodeScannerController? controller) =>
                            CustomOverlay(
                              controller: controller!,
                              result: result,
                            ),
                      ),
                    ),
                  ),
                  SizedBox(height: 20),
                  Expanded(
                    child: Text(
                      result ?? 'No result',
                      style: TextStyle(fontSize: 24),
                      textAlign: TextAlign.center,
                    ),
                  ),
                ],
              )
            : Center(
                child: ElevatedButton(
                  onPressed: () async {
                    await _requestPermission();
                  },
                  child: Text('请求相机权限'),
                ),
              ),
      ),
    );
  }
}

class CustomOverlay extends StatelessWidget {
  final CodeScannerController controller;
  final String? result;

  CustomOverlay({required this.controller, required this.result});

  @override
  Widget build(BuildContext context) {
    var color = Colors.red.withOpacity(0.5);

    return Stack(
      alignment: Alignment.center,
      children: <Widget>[
        Positioned(
          left: 0,
          right: 0,
          top: 0,
          bottom: 0,
          child: Opacity(
            opacity: controller.value.isScanning ? 0.5 : 0.0,
            child: ColorFiltered(
              colorFilter: ColorFilter.mode(color, BlendMode.darken),
              child: FlutterLogo(size: 100),
            ),
          ),
        ),
        if (controller.value.isScanning)
          Positioned(
            bottom: 10,
            left: 0,
            right: 0,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'Scanning...',
                  style: TextStyle(color: Colors.white, fontSize: 20),
                ),
              ],
            ),
          ),
        if (result != null)
          Positioned(
            bottom: 80,
            left: 20,
            right: 20,
            child: Card(
              elevation: 8,
              child: Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text(
                  'Result: $result',
                  style: TextStyle(fontSize: 24, color: Colors.black),
                ),
              ),
            ),
          ),
      ],
    );
  }
}

解释

  1. 权限请求:使用permission_handler插件请求相机权限。
  2. 初始化CodeScannerController:在initState方法中初始化控制器并监听扫描结果。
  3. UI布局:根据权限状态显示不同的UI。如果权限已授予,则显示二维码扫描视图和扫描结果;否则,显示请求权限的按钮。
  4. 自定义覆盖层:使用CustomOverlay小部件自定义扫描视图上的覆盖层,包括扫描提示和结果显示。

这个示例展示了如何在Flutter应用中集成并使用code_scanner插件进行二维码扫描。请确保在实际项目中处理错误和异常情况,并根据需要进行UI调整。

回到顶部