1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

スクリーンショット 2024-07-03 15.33.27(2).png

記事の内容に間違いがあるかもしれません。僕自身もまだまだ学習中なため、コメントをお気軽にください🙇

はじめに

バーコードの読み取りで有名ないPackagesというと、mobile_scannerのイメージがあると思っています。
しかし、mobile_scannerをしても、iOSとAndroidで読み取れないものがあったりしたため、アプリに使用する上では採用するかどうかが悩ましかったです。
そのため、別のPackagesを検討しました。

flutter_zxingについて

  • ZXing (Zebra Crossing) バーコード スキャン ライブラリを使用して、QRコードやバーコードを読み取れる
  • QRコードの生成もできる

以下の2つを使用して実装されている

  • Dart FFI (Foreign Function Interface)
  • zxing-cppライブラリ

Dart FFI (Foreign Function Interface)

  • Dart言語からCやC++などを呼び出すための仕組み
  • zxing-cppがC++で書かれており、それを呼び出すためのもの

zxing-cppライブラリ

  • C++で実装されている
  • 線形バーコード(1Dバーコード)やマトリックスバーコード(2Dバーコード)の画像の処理ができる
  • 様々な形式のバーコードの読み取りと書き込みができる

線形バーコードやマトリックスバーコードについては以下に例を掲載しています。

線形バーコード マトリックスバーコード

サポートしているフォーマット

Linear product  Linear industrial Matrix
UPC-A Code 39 QR Code
UPC-E Code 93 Micro QR Code
EAN-8 Code 128 rMQR Code
EAN-13 Codabar Aztec
DataBar DataBar Expanded DataMatrix
- ITF -
- - MaxiCode (partial)

サポートされているプラ​​ットフォーム

Android (最低 API レベル 21)
iOS (iOS 11.0以上)
MacOS (最低 OSX 10.15) (ベータ版)
Linux (動作しますが、カメラはサポートされていません) (アルファ版)

警告
lutter_zxing は Dart FFI (Foreign Function Interface) 機能に依存しており、現在はモバイルおよびデスクトップ プラットフォームでのみ使用可能です

バーコードを読み取れるようにする

1、Packagesを導入

dependencies:
  flutter_zxing: ^1.5.2

2、初期設定

iOS

info.plist
<key>NSCameraUsageDescription</key>
<string>バーコードの読み取りのために使用</string>

Android

build.gradle
  minSdkVersion 21

3、読み取る実装を記載してみる

サンプルコードを参考にさせていただきました。

main.dart
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_zxing/flutter_zxing.dart';

import 'widgets/debug_info_widget.dart';
import 'widgets/scan_result_widget.dart';
import 'widgets/unsupported_platform_widget.dart';

void main() {
  zx.setLogEnabled(kDebugMode);
  // print zxing version
  debugPrint('ZXing version:  ${zx.version()}');
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Zxing Example',
      debugShowCheckedModeBanner: false,
      home: DemoPage(),
    );
  }
}

class DemoPage extends StatefulWidget {
  const DemoPage({super.key});

  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  Uint8List? createdCodeBytes;

  Code? result;
  Codes? multiResult;

  bool isMultiScan = false;

  bool showDebugInfo = true;
  int successScans = 0;
  int failedScans = 0;

  @override
  Widget build(BuildContext context) {
    final isCameraSupported = defaultTargetPlatform == TargetPlatform.iOS ||
        defaultTargetPlatform == TargetPlatform.android;
    return DefaultTabController(
      length: 2,
      child: Scaffold(
        appBar: AppBar(
          title: const TabBar(
            tabs: [
              Tab(text: 'Scan Code'),
              Tab(text: 'Create Code'),
            ],
          ),
        ),
        body: TabBarView(
          physics: const NeverScrollableScrollPhysics(),
          children: [
            if (kIsWeb)
              const UnsupportedPlatformWidget()
            else if (!isCameraSupported)
              const Center(
                child: Text('Camera not supported on this platform'),
              )
            else if (result != null && result?.isValid == true)
              ScanResultWidget(
                result: result,
                onScanAgain: () => setState(() => result = null),
              )
            else
              Stack(
                children: [
                  ReaderWidget(
                    onScan: _onScanSuccess,
                    onScanFailure: _onScanFailure,
                    onMultiScan: _onMultiScanSuccess,
                    onMultiScanFailure: _onMultiScanFailure,
                    onMultiScanModeChanged: _onMultiScanModeChanged,
                    onControllerCreated: _onControllerCreated,
                    isMultiScan: isMultiScan,
                    scanDelay: Duration(milliseconds: isMultiScan ? 50 : 500),
                    resolution: ResolutionPreset.high,
                    lensDirection: CameraLensDirection.back,
                    flashOnIcon: const Icon(Icons.flash_on),
                    flashOffIcon: const Icon(Icons.flash_off),
                    flashAlwaysIcon: const Icon(Icons.flash_on),
                    flashAutoIcon: const Icon(Icons.flash_auto),
                    galleryIcon: const Icon(Icons.photo_library),
                    toggleCameraIcon: const Icon(Icons.switch_camera),
                    actionButtonsBackgroundBorderRadius:
                        BorderRadius.circular(10),
                    actionButtonsBackgroundColor: Colors.black.withOpacity(0.5),
                  ),
                  if (showDebugInfo)
                    DebugInfoWidget(
                      successScans: successScans,
                      failedScans: failedScans,
                      error: isMultiScan ? multiResult?.error : result?.error,
                      duration: isMultiScan
                          ? multiResult?.duration ?? 0
                          : result?.duration ?? 0,
                      onReset: _onReset,
                    ),
                ],
              ),
            if (kIsWeb)
              const UnsupportedPlatformWidget()
            else
              ListView(
                children: [
                  WriterWidget(
                    messages: const Messages(
                      createButton: 'Create Code',
                    ),
                    onSuccess: (result, bytes) {
                      setState(() {
                        createdCodeBytes = bytes;
                      });
                    },
                    onError: (error) {
                      _showMessage(context, 'Error: $error');
                    },
                  ),
                  if (createdCodeBytes != null)
                    Image.memory(createdCodeBytes ?? Uint8List(0), height: 400),
                ],
              ),
          ],
        ),
      ),
    );
  }

  void _onControllerCreated(_, Exception? error) {
    if (error != null) {
      // Handle permission or unknown errors
      _showMessage(context, 'Error: $error');
    }
  }

  _onScanSuccess(Code? code) {
    setState(() {
      successScans++;
      result = code;
    });
  }

  _onScanFailure(Code? code) {
    setState(() {
      failedScans++;
      result = code;
    });
    if (code?.error?.isNotEmpty == true) {
      _showMessage(context, 'Error: ${code?.error}');
    }
  }

  _onMultiScanSuccess(Codes codes) {
    setState(() {
      successScans++;
      multiResult = codes;
    });
  }

  _onMultiScanFailure(Codes result) {
    setState(() {
      failedScans++;
      multiResult = result;
    });
    if (result.codes.isNotEmpty == true) {
      _showMessage(context, 'Error: ${result.codes.first.error}');
    }
  }

  _onMultiScanModeChanged(bool isMultiScan) {
    setState(() {
      this.isMultiScan = isMultiScan;
    });
  }

  _showMessage(BuildContext context, String message) {
    ScaffoldMessenger.of(context).hideCurrentSnackBar();
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message)),
    );
  }

  _onReset() {
    setState(() {
      successScans = 0;
      failedScans = 0;
    });
  }
}

最後に

ちゃんとしたバーコードの読み取りはなかなか難しいなと感じました👀
今後も色々と調査してみたいところですね。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?