有没有办法在 Flutter 中扫描条形码?

Is there a way to scan barcodes in Flutter?

基本上,我正在制作一个扫描二维码以连接到服务器的应用程序。然后,应用程序将扫描商品的条形码并拍摄商品的照片并将其发送到服务器。我的问题如下:

有没有Flutter插件扫描二维码和条码输入不冲突image_picker

这是我目前的发现。

感谢您提供的任何帮助。谢谢!


更新

barcode_scan 的问题已解决。我最终使用了这个,因为它比公认的答案更快,而且它的问题很快就得到了解决。请注意,它在 iOS 上的行为已被 Apple 修改,因此您可能会得到校验和数字或其他内容的不同结果。

我目前正在做一些与我的 QR 生成插件 (https://github.com/lukef/qr.flutter) 配套的事情,但不幸的是我没有具体的时间表。

我的计划是使用 Texture 对象并连接一个相机(或分叉/使用相机插件),然后使用 Google Vision API(https://developers.google.com/vision/android/barcodes-overview).

应该是小菜一碟,找个时间就好了。无论哪种方式,如果您想这样做,这就是计划:)

我以前也遇到过类似的问题,按照你的方法找了很多也没找到。我决定最好的方法是自己编写一个插件...在这里为我的插件添加如此无耻的插件 =D,并不是说我从任何其他使用它的人那里受益。

可以看到here。然而,我没有时间记录它,广泛地测试它,或者在 Pub 上正确地发布它。所以你的里程可能会有所不同。但是,它应该可以在 android 4.4+(可能更低)和支持 flutter 的 iOS 设备上工作。我也没有结合相机插件对其进行测试,但我不明白为什么它会出现问题。

它采用了与大多数其他二维码插件不同的方法;它不是进行 android 或 iOS window,进行扫描,然后返回 flutter,而是使用 flutter 的纹理渲染功能让相机直接渲染成 flutter。

还有一些需要考虑的事情是,它使用 Google Mobile Vision SDK 以及随附的适用许可和功能(并且需要 Android 上最新版本的 Play 服务);而且它目前只支持从条形码扫描中提取最基本的信息——我只需要原始文本,所以这就是我所做的。

要使用它,请将其添加到您的 pubspec.yaml:

dependencies:
  qr_mobile_vision: '^0.0.7'

并执行如下:

import 'package:qr_mobile_vision/QrCamera.dart';

...

new Container(
  constraints: new BoxConstraints.loose(
  new Size(cameraSize, cameraSize)),
  child: new QrCamera(
    qrCodeCallback: (code) {
      print(code);
    }
  ),
)

我确实计划最终完成 documentation/testing/etc,但欢迎您在此期间尝试一下。如果您决定使用它并且需要它不支持的功能,我可能能够帮助实现它...但是欢迎并鼓励 PR!

更新: 这现在包括条形码支持。你可以在实例化QrCamera时传入你想要支持的二维码/条形码类型。它默认为全部,这需要更多的处理,所以如果你追求某种类型,建议你传入它。

您可以使用开源 SDK(例如 ZXing)或商业 SDK(例如 Dynamsoft Barcode Reader SDK) 在你的 Flutter 项目中。实现条码扫描功能很容易。

我写了一篇文章 - Flutter Programming with Android AAR File, sharing how to scan QR code in a flutter project. The source code 也可以在 GitHub 上找到。

Java代码

private String onGetBarcode(String json) {
        String filename;
        try {
            JSONObject message = new JSONObject(json);
            filename = message.getString("filename");
        } catch (JSONException e) {
            Log.e(TAG, "JSON exception", e);
            return null;
        }

        String locationProvider;
        String barcodeResult = "No barcode detected";
        File file = new File(filename);
        if (!file.exists()) {
            barcodeResult = "No file exists: " + file.toString();
            Toast.makeText(BarcodeReaderActivity.this, barcodeResult, Toast.LENGTH_LONG).show();

            return null;
        }
        else {
            Bitmap bitmap = BitmapFactory.decodeFile(file.toString());
            BarcodeReader reader = new BarcodeReader("license");
            ReadResult result = reader.readSingle(bitmap, Barcode.QR_CODE);
            Barcode[] all = result.barcodes;
            if (all != null && all.length == 1) {
                barcodeResult = all[0].displayValue;
            }
            else {
                barcodeResult = "no barcode found: " + file.toString();
            }

            bitmap.recycle();

        }

        JSONObject reply = new JSONObject();
        try {
            if (barcodeResult != null) {
              reply.put("result", barcodeResult);
            } else {
              reply.put("result", "No barcode detected");
            }
        } catch (JSONException e) {
            Log.e(TAG, "JSON exception", e);
            return null;
        }

        return reply.toString();
    }

飞镖代码

@override
  Widget build(BuildContext context) {
    if (_isExisted) {
      return new Material(
          child: new Center(
              child: new Column(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                    new Text('Barcode Reader'),
                    new Input(
                      labelText: 'Please input the image path',
                      value: new InputValue(text: _filename),
                      onChanged: onTextChanged,
                      autofocus: true,
                    ),
                    new Row(
                        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                        children: <Widget>[
                          new RaisedButton(
                              child: new Text('Read'),
                              onPressed: _getBarcode
                          ),
                          new RaisedButton(
                              child: new Text('Reset'),
                              onPressed: _resetResult
                          ),
                        ]
                    ),
                    new Image.file(new File(_filename)),
                    new Text('$_result'),
                  ]
              )
          )
      );
    }
    else {
      return new Material(
          child: new Center(
              child: new Column(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                    new Text('Barcode Reader'),
                    new Input(
                      labelText: 'Please input the image path',
                      onChanged: onTextChanged,
                      autofocus: true,
                    ),
                    new Row(
                        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                        children: <Widget>[
                          new RaisedButton(
                              child: new Text('Read'),
                              onPressed: _getBarcode
                          ),
                          new RaisedButton(
                              child: new Text('Reset'),
                              onPressed: _resetResult
                          ),
                        ]
                    ),
                    new Text('$_result'),
                  ]
              )
          )
      );
    }
  }

  Future<Null> _readBarcode() async {
    final Map<String, String> message = <String, String>{'filename':_filename};
    final Map<String, dynamic> reply = await HostMessages.sendJSON('getBarcode', message);
    // If the widget was removed from the tree while the message was in flight,
    // we want to discard the reply rather than calling setState to update our
    // non-existent appearance.
    if (!mounted)
    return;
    setState(() {
    _result = reply['result'].toString();
    });
  }

截图

所以花一点时间自己做:)

根据我的研究,有一些不错的选择:

  1. flutter_barcode_scanner (reader)
  2. barcode_scan (reader)
  3. qr_flutter(创建二维码)

1st:barcode_scan 将作为两个常用的 iOS 和 Android 库的包装器。 (iOS: https://github.com/mikebuss/MTBBarcodeScanner, Android: https://github.com/dm77/barcodescanner)

它们已经是第 3 版了,似乎所有主要错误都已解决。

dependencies:
  barcode_scan: ^3.0.1

这里是功能列表:

[x] Scan 2D barcodes
[x] Scan QR codes
[x] Control the flash while scanning
[x] Permission handling

主要警告是关于 https://github.com/dm77/barcodescanner 不再维护的。

第二个选项是 flutter_barcode_scanner,它也适用于 android 和 ios。 https://pub.dev/packages/flutter_barcode_scanner 对于 android 只是放置 pubdev 依赖并运行,对于 iOS 您唯一需要做的是:1:将最小部署目标设置为 11,2:将 Swift 版本设置为 5 3:请求使用相机的许可。可能 flutter-bardode-scanner 是最好的选择,因为它只依赖于 pubdev 依赖而不需要第三方项目(不再维护)。

当需要创建 QR 码时,有:QR.Flutter 也适用于 ios 和 android。 特点:

[x] Built on QR - Dart
[x] Automatic QR code version/type detection or manual entry
[x] Supports QR code versions 1 - 40
[x] Error correction / redundancy
[x] Configurable output size, padding, background and foreground colors
[x] Supports image overlays
[x] Export to image data to save to file or use in memory
[x] No internet connection required

他们两人在 pub.dev 上的这个求婚声望最高。因此,您可能需要尝试一下,看看哪些可以满足您的项目需求。

更新答案:要从图片库中读取条形码,有一个名为 qr_code_tools 的替代包。图片可以从ImagePicker中获取,然后通过QrCodeToolsPlugin.decodeFrom

解码成数据

从厨房获取图像:

ImagePicker.pickImage(source: ImageSource.gallery));

将图像解码为数据:

import 'package:qr_code_tools/qr_code_tools.dart';

String _data;
Future decode(String file) async {
  String data = await QrCodeToolsPlugin.decodeFrom(file);
  setState(() {
    _data = data;
  });
}

我用了qr_scan。问题是我用 objective c 创建了我的 flutter。我不得不删除 iOS 并更改它。

为此,从项目中删除 ios 文件夹并使用 swift 创建它。

创建使用是命令 flutter create -i swift . 最后不要忘记 . 因为我花了几个小时才弄明白