JINSテックブログ
🚀

爆速スキャンを実現!FlutterとWebviewでAsReaderを使い倒す連携テクニック

に公開

0. 初回投稿のご挨拶

皆さん、お疲れ様です!

この度2025年10月1日JINSに入社いたしました、タン(牛タン?豚タン?どちらでもお好きな方でお願いします!)です!

入社して初めて技術記事を書いてみました!正直、まだ慣れないことばかりですが、お手柔らかに読んでいただけると嬉しいです!

ここで、私のフレッシュな気合を込めて、皆さんに謎かけを一つ!🧠🧠🧠
「皆さんと働くこと」 とかけまして、
「机の次に欲しいもの」 と解きます。

そのココロは...(答えは一番最後で!)


実は私、今回開発でAsReaderっていう製品を初めて触りました!正直、最初は「ふーん、専用リーダーね」くらいに思ってたのですが、実際に使ってみてビックリしました😲。普段、スマホのカメラでQRコードを読み取る時に「あー、ピントが…」「ちょっと反射が…」って手間取ること、ありますよね?(あると信じたい)でも、AsReaderはそんなの一切関係なし! スマホより遥かに高速で動ける、まさに現場の救世主です。

そんな高性能のAsReader連携のテクニックを、今回は皆さんにご紹介したくて筆をとりました。 「爆速」 なんて、ちょっと大げさで恥ずかしいですが、この連携パターン、他の開発にも超使えます!

今回は、そんな 「AsReader(アズリーダー)」 を、FlutterとWebviewを使ってスマートに連携させるテクニックを、コードを交えて分かりやすく解説します。


1. AsReaderって何?なんでスマホのカメラは遅いの?

AsReader(アズリーダー)とは

AsReaderオフィシャルサイト

一言と言えば、AsReaderは、スマートフォンと一体化して使用できるバーコード/RFIDリーダーライターです。専用のケースでiPhoneやAndroidなどのスマートフォンに装着することで、スマートフォンがプロ仕様のハンディターミナルに変身します。

業務用のハンディターミナルと同等の高い読み取り精度高速性を実現しながら、コンシューマー向けのスマートフォンを活用するため、コストを抑えられるのが大きな魅力です。小売、物流、医療、製造など、幅広い現場で活躍しています。

オフィシャルサイトに開発者向けの情報もたくさん載っているので、メソッドのパラメータや戻り値なども簡単に調べられます!


📱 スマホのカメラでスキャンが遅い理由

「スマホのカメラでもスキャンできるじゃん?」って思いますよね。でも、スマホのカメラは「綺麗な写真を撮ること」がメイン機能。業務で求められる「一瞬で、確実に」読み取るのには向いていません。

  1. 読み取り性能の限界:
    • スマホのカメラは、照明環境バーコードの状態に影響を受けやすく、読み取りに時間がかかったり、失敗したりすることが多いです。
    • AsReaderのような専用リーダーは、強力なスキャンエンジン最適化された照明を備えており、一瞬で正確に読み取ることができ、ハンディターミナルと同等の読み取り性能を持っています。
  2. オートフォーカスによる遅延:
    • スマホカメラはバーコードにピントを合わせるためのオートフォーカスが必要で、この処理に時間がかかります。
    • AsReaderはピント合わせの工程を必要としないか、極めて高速な専用機構を持っているため、爆速スキャンが可能です。
  3. 操作性:
    • スマホカメラでスキャンする場合、アプリを起動し、画面上でバーコードを認識させる手間が発生します。AsReaderはトリガーボタンを押すだけで即座にスキャンできるため、現場での作業効率が格段に向上します。

つまり、一瞬の読み取り速度高い安定性が求められる業務現場では、専用機であるAsReaderの出番というわけです!


2. システムの構成図とアプリ画面イメージ

今回実現したシステムの構成と、実際のアプリ画面のイメージをご紹介します。

アプリ画面のイメージ

接続前(Web画面) スキャン中 スキャン完了(Web画面)
FlutterのWebviewにHTMLを表示
(ここではまだスキャン前)
AsReader本体のトリガーを実行
(爆速スキャン中!AsReaderが光る)
結果がWeb画面のテキストフィールドに反映
(爆速でデータ入力完了)

システム構成とスキャン過程

構成要素 動作 処理の内容
Webview (HTML/JS) ① スキャン開始要求 画面の📱ボタンをタップし、AsReaderChannel.postMessageでFlutterにメッセージを送信。
Flutter (Dart) ② ネイティブ機能呼び出し Webからのメッセージを受信し、AsReaderのネイティブSDKを通じてスキャンを開始。
AsReader 本体 ③ バーコード読み取り 高性能スキャンエンジンがバーコードを読み取り、結果をバイト列でFlutterに返す。
Flutter (Dart) ④ 結果の処理と送信 バイト列を文字コード判定(UTF-8, Shift_JIS等)で正確にデコード。window.postMessageで結果をWebviewに送信。
Webview (HTML/JS) ⑤ 画面に結果表示 window.addEventListener('message', ...)で結果を受信し、入力フィールドに表示。

3. FlutterとWebviewの連携手法とその選択理由

Webviewとネイティブコード(今回はFlutter)の間でデータをやり取りする方法はいくつかあります。それぞれの特徴と、今回「PostMessage」を採用した理由を解説します。

Webview連携の主な手法

手法 WebからNative NativeからWeb 特徴
JavaScript Channel 使用 (AsReaderChannel.postMessage) 非推奨 (一方通行) Flutterで推奨される方法。Web側の処理がシンプルで、Native側でDartコードとしてメッセージを受信できる。ただしNative \rightarrow Webの通知は別途工夫が必要。
PostMessage + JSON 使用 (postMessage) 使用 (window.postMessage) Webの標準API。双方向通信が可能で、セキュリティも高い。Web側が汎用的なため、他の環境への移植性が高い。
WebView URL Scheme 使用 (location.href) 使用 (evaluateJavascript) Web側でURLを偽装し、Native側でURL変更を監視。簡易的なメッセージングに使えるが、複雑なデータ送信や連続通信には不向き。
Window 関数呼び出し 非推奨 使用 (evaluateJavascript) Native側からWeb側のグローバル関数を直接呼び出す。NativeからWebへのシンプルなコマンド送信に便利。Web側のセキュリティリスクに注意。

PostMessageとは?

PostMessage(正確にはwindow.postMessage())は、Webの標準的なAPI(Application Programming Interface)の一つで、 異なるオリジン(ドメイン、プロトコル、ポート) を持つウィンドウやフレーム間で安全にデータをやり取りするために設計されています。

Webview環境では、このAPIを利用して「Webview内のWebコンテンツ」と「それを表示しているNative/Flutterアプリ」間でメッセージ(通常はJSON形式の文字列)を双方向で安全に受け渡しできます。

PostMessageを選んだ理由

今回の実装では、主に以下の理由からPostMessageを選択し、JavaScript Channel(Web \rightarrow Native)とPostMessage(Native \rightarrow Web)を併用する形を採用しています。

  1. Native \rightarrow Webへの汎用性と移植性:
    • スキャン結果をWeb画面に返す際、postMessageはWebの標準機能です。もし将来的にこのWeb画面を別の環境(例:Electron、別のNativeフレームワーク)に移植する場合でも、Web側コードの変更が最小限で済みます
  2. 双方向通信の実現:
    • スキャン要求(Web \rightarrow Native)は、Flutterが提供するJavaScript Channel (AsReaderChannel)を使用しました。これはFlutter環境では最もシンプルで安定したNative呼び出し方法だからです。
    • スキャン結果(Native \rightarrow Web)は、PostMessage (window.postMessage)を使用しました。これにより、Native側で処理した結果を、Web側の標準的なイベントリスナー (window.addEventListener('message', ...))で安全に受け取ることができます。
  3. データ形式の統一:
    • postMessageはJSON形式の文字列でのデータ送信に適しており、複雑なスキャン結果やステータス情報も一貫した形式でやり取りできます。

4. AsReaderスキャンのコア実装(Flutter/Dart)

爆速スキャンを実現するための、Flutter(Dart)側のAsReader SDKを使ったコアな処理を紹介します。

a. スキャン開始処理 (_startAsReaderScan)

Web側から「スキャン開始」のメッセージを受け取ったとき、この関数が呼ばれます。

// main.dart (Dart)
Future<void> _startAsReaderScan() async {
    if (!_isReaderReady) {
      // Readerが未接続なら、まず電源ON(起動)を試みる
      int res = await _asreader.setReaderPower(true, false, false, true, true, true, 0);
      print("起動結果: $res");
      return;
    }
    
    // Reader準備OKなら、すぐにバーコードスキャンを開始!
    _asreader.startBarcodeScan(0, 0); 
    
    // Webviewに「スキャン開始したよ」と通知
    _sendScanStarted(); 
}
  • _asreader.setReaderPower:AsReaderの電源をONにし、接続を確立するための重要なSDK関数です。
  • _asreader.startBarcodeScan(0, 0):この命令で、物理的なスキャンが開始されます。トリガーが押された時や、トリガーなしでソフトウェア的にスキャンを開始する場合に使われます。

b. スキャンデータ受信処理 (_handleScanData)

スキャンが成功すると、Native SDKからこのコールバック関数にデータ(バイト列)が渡されます。

// main.dart (Dart)
void _handleScanData(Uint8List byteData) {
    _stopScan(); // 読み取ったらすぐにスキャンを停止
    
    // バイト列を正確な文字列に変換
    final String scanResult = _decodeBarcodeData(byteData);
    
    // PostMessage APIを使用してWebViewにスキャン結果を送信
    _sendScanCompleted(scanResult);
} 
  • _stopScan():一般的に、バーコードを1つ読み取ったら、意図しない連続スキャンを防ぐためにすぐに停止させます。
  • _decodeBarcodeData(byteData):この関数内で、前述のUTF-8/Shift_JIS自動判別によるデコード処理が行われ、Webviewに渡せるきれいな文字列に変換されます。

5. FlutterとWebviewで実現するシームレス連携

なぜFlutterとWebviewを使うのか?

AsReaderを動かすには、そのデバイス(今回はiOSを想定)に接続するための ネイティブSDK(Software Development Kit) の利用が必須です。

  • AsReader SDK \rightarrow ネイティブ機能
  • Webシステム \rightarrow Web画面

業務システム自体がすでにWebアプリケーションとして構築されている場合、ネイティブアプリにすべて作り直すのは大変です。そこで、FlutterWebviewの組み合わせが活きてきます。

  1. Flutter: iOSのネイティブSDKを呼び出すための 「器」 として機能します。
  2. Webview: 既存の Web画面(HTML/JavaScript) をそのまま表示するために使用します。

この構成により、「Webの柔軟性」と「ネイティブ機能(AsReader)の強力さ」を両立したアプリケーションが構築できます。


6. 実装のポイント解説(コード抜粋)

今回の連携を実現するために重要な、FlutterとWebview間のメッセージングと文字コード処理のポイントを解説します。

a. FlutterでWebviewとチャンネルを設定 (main.dart)

Webviewの初期化で、JavaScriptとの通信路を確立します。

// Dart
void _initWebView() {
    _controller = WebViewController()
      // ...
      ..addJavaScriptChannel(
        'AsReaderChannel', // Web -> Nativeのメッセージ受信窓口
        onMessageReceived: (JavaScriptMessage message) {
          _handleWebViewMessage(message.message); // Webからのメッセージを処理
        },
      )
      ..loadFlutterAsset('assets/index.html');
}

b. JavaScriptからDartへのメッセージ送信(JavaScript Channel) (index.html)

Web側でスキャンボタンが押された時、AsReaderChannel.postMessageを使って、文字列化されたJSONをFlutterに渡しています。

// JavaScript (index.html)
// Flutterにメッセージを送信 (JavaScript Channelを使用)
sendToFlutter(message) {
    if (window.AsReaderChannel) {
        AsReaderChannel.postMessage(JSON.stringify(message)); 
    }
    // ...
}

c. FlutterからJavaScriptへのメッセージ送信(PostMessage) (main.dart)

スキャン結果をWebviewに返す際は、PostMessage APIを使用し、Web側のwindow.addEventListener('message', ...)で受信させます。

// Dart
// WebViewにメッセージを送信(PostMessage API使用)
void _sendMessageToWebView(Map<String, dynamic> message) {
    final jsonMessage = jsonEncode(message);
    // Web側のイベントリスナーで受信
    _controller.runJavaScript('window.postMessage($jsonMessage, "*");');
}

d. スキャン結果の文字エンコーディング処理 (main.dart)

バーコードのデータはバイト列で渡されるため、文字化けを防ぐためのデコード処理が重要です。main.dartでは、多言語対応のためにUTF-8Shift_JISLatin-1の順でデコードを試みています。

// Dart(_decodeBarcodeData関数から抜粋)
// 1. UTF-8を最初に試行
try { /* ... */ } catch (_) { /* ... */ }

// 2. Shift_JISを試行
try {
  final shiftJisResult = shiftJis.decode(cleanData); // charsetパッケージを使用
  // ...
} catch (_) { /* ... */ }
// ...

7. さいごに

今回のAsReader連携を通じて、Native機能の呼び出しやWebViewとの安全なデータ通信、文字コード処理など、シンプルながら多くの技術的な知識を学ぶことができました。特に、FlutterのJavaScript ChannelWebの標準PostMessage APIを組み合わせた双方向通信のパターンは、他のネイティブ連携が必要なFlutter開発にも応用できる非常に有用なテクニックだと感じています。

この記事が、皆さんのFlutter開発や、専用デバイス連携のヒントになれば嬉しいです!


最後に、謎かけの答えです!

「皆さんと働くこと」 とかけまして、
「机の次に欲しいもの」 と解きます。

そのココロは...

「いいっす(椅子)ね!」

しょうもないダジャレにお付き合いいただき、ありがとうございました! 皆さんに少しでも楽しんでもらえたなら嬉しいです!

JINSテックブログ
JINSテックブログ

Discussion