🐥

Flutter で QRコード印刷してみた

2023/07/09に公開

まーじで、大変だった。
とりま、ここに結果だけ入れとく。

https://youtu.be/I88S-wTnAh8

使用したデバイス

https://www.sii.co.jp/sps/product/unit/mp-b20/index.html

↑ AirレジとかStoreとかで使用されてるっぽい。

確かにモバイルプリンターって、便利ですよね。
モバイルなので、USBとかで固定する必要がないし配線も不要。
BlueToothあれば印刷出来るって画期的。

使用した専用アプリ

https://apps.apple.com/jp/app/sii-url-print-agent/id1502527506
https://play.google.com/store/apps/details?id=com.seikoinstruments.siiurlprintagent&hl=ja&gl=US

↑モバイルプリンターを提供している会社がアプリを出しててそれを使った

え、専用アプリ使わないといけないの!?
と思ったかもしれませんが、

もちろん、iOS、android sdkもあって、
それらを自社アプリに組み込めば専用アプリなんて不要です。

ですが、Flutter しか使用した事が無かった自分は
ネイティブのsdkなんて使える訳もなく
専用アプリを使うしかないという悲しい結論になりました。

ただ、将来的にはネイティブの実装をちゃんと調べて
それを元に Flutter sdk をこちらが作ってしまって
自社アプリに入れる予定です。

具体的に何をしたのか?

Flutter で PDF を作成して base64 に変換して
それを URL Scheme 使って専用アプリに飛ばす

って事をしました。

という訳でここからコード↓

PDFを作成するために

① こんな感じでまずはPDFを作成する

PDF作成処理
import 'package:pdf/widgets.dart' as pw;

/// PDF作成処理
Future<pw.Document> createPdf() async {

  /// フォントを設定
  final fontData = await rootBundle.load('assets/NotoSansJP-Regular.ttf');
  final font = pw.Font.ttf(fontData);

  /// 基盤となるPDFを追加、これから追加していく
  final pdf = pw.Document(author: 'Me');

  /// QRコードを作成
  var qrCode = pw.BarcodeWidget(
    color: PdfColors.black,
    barcode: pw.Barcode.qrCode(),
    data: 'https://google.com/',
    width: 72,
    height: 72,
  );

  // 表紙
  final cover = pw.Page(
    pageTheme: pw.PageTheme(
      theme: pw.ThemeData.withFont(base: font),
      pageFormat: const PdfPageFormat(
        72 * 2,
        72 * 2 + 20,
      ),
    ),
    build: (context) {
      return pw.Column(
        children: [
          pw.Text(
            '注文受付QRコード',
            style: const pw.TextStyle(fontSize: 10),
          ),
          pw.Text(
            'アプリをインストールして使えます!!',
            style: const pw.TextStyle(
              fontSize: 8,
            ),
          ),
          pw.SizedBox(height: 4),
          qrCode,
         ],
       );
     },
   );
 
  pdf.addPage(cover);

  return pdf;
}

② 出来た PDF を base64 に変換する

PDF → base64 に変換
onPressed: () async {  
  // PDFをバイトデータに変換
  final pdfBytes = await pdf.save();

  // バイトデータをBase64に変換
  final base64 = base64Encode(pdfBytes);

  // 専用アプリに飛ばす
  _lanchUrl(base64);
},

③ URL Scheme 使って専用アプリに飛ばす

void _lanchUrl() {
  const String baseUrl = 'siiprintagent://1.0/print?';
  final List<String> paramlist = [
    'BtKeepConnect=always',
    'Format=pdf',
    'Data=$base64',
  ];
  
  final String params = paramlist.join('&');
  final uri = Uri.parse(baseUrl + params);
  
  await launchUrl(uri);
}

すると勝手にアプリが検知して自動で印刷してくれる

何が大変だったか

A. 公式ドキュメントに重大なミスがあった事

これがマジで一番の問題だった。
『公式がミスるなよぉぉぉぉ』
と言いたくなりますがしょうがない。

だって、作成者は人間だもの。

その公式ドキュメントはこちら
https://www.sii-ps.com/common/URLPRNAgent_Android_iOS_JA_01.pdf

具体的にどこをミスってたのか?

1点目:
host と記載がある箇所
→ 正確には version でした。

まぁ、これは別に大した問題ではない気がするし
下に version の説明があるから、もしかしたら、、、って気付ける可能性は十分にある。

というか、ワンチャン、そういう書き方の方が適切なのかもしれない。
この辺は僕の勉強不足の可能性あり。

2点目:
クエリのカンマ(,)が本当はアンド(&)を使用する必要があった。

これはマジでヤバかった。
マジで普通にドキュメント見ながら進めてたら一生終わらんかったと思うw

まぁ、クエリとか慣れてる人なら

「アンド(&)じゃないんだぁ」
「あれ、動かない...」
「ワンチャン、アンド(&)かも...」
「動いた!!!!」

ってなるかもしれない。

けど、俺はそうはならなかった。

てか、実際結構慣れてないと無理な気がする

どうやって解決したのか?

このドキュメントにはサンプルもなくて
例となるURLが見当たらなかった。

つまり、説明書であって実際に動かすためのサンプルでは無かった。

ということで、まずはサンプル使って実際に動かして
そこから自分なりにカスタマイズしていこう

こういう手順に切り替えて
ひたすらサンプルを探してみた。

そんな中たまたま見つけたサイトがこちら

https://www.iricode.net/memo/print_pdf_with_mp-b20/297/

この方は普通にサンプルを見つけて
Webで実装してた。

つまり、Webにサンプルがあるんだぁと思って
探してると、、、あった

で、この JA の方を開くと、

こんな感じでサンプルを見つけれた。

あとは簡単。
「検証」から印刷してる処理を見る。

そしたら気付いたんだよね。

カンマ(,)じゃなくてアンド(&)使ってる

って事に。

あれ、Webではアンド(&)って書いてて、ドキュメントにはカンマ(,)って書いてる。
よし、両方試せるように仕組み化しよう

(...コードを書き書き)

で、テスト。

よし、動いた。

こういう感じだった。

最後に

まぁ、最後に一言。

ドキュメントよりも実際に使われてるコードの方が信憑性が高い『場合』もある

逆算大事。

Discussion