記事の内容に間違いがあるかもしれません。僕自身もまだまだ学習中なため、コメントをお気軽にください🙇
はじめに
バーコードの読み取りで有名ない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;
});
}
}
最後に
ちゃんとしたバーコードの読み取りはなかなか難しいなと感じました👀
今後も色々と調査してみたいところですね。