UUUO Tech Blog

水産流通のDXや業務効率化を実現するアプリケーション「UUUO」「atohama」を開発・展開している株式会社ウーオのテックブログです。プロダクトにまつわる様々な情報発信を行います。

Flutter WebでPDFファイルを生成して印刷

ウーオでアプリ(UUUO, UUUO Sellers)の開発を担当している榎本です。

ウーオでは、お魚を出品したい方向けの「UUUO Sellers」というアプリを提供しています。 以前、こちらの記事で、Web版のUUUO SellersをFlutter Webで作った話をしました。 今回の記事は、消費地市場に届いた荷物の配送情報の連携に用いる配送指示書と呼ばれる書類をPDFとして出力する機能をWeb版のUUUO Sellersに追加した話です。

産地市場と消費地市場

配送指示書

環境

実装

pdfライブラリ、printingライブラリの導入

FlutterでPDFを生成したい場合は、pdfライブラリが便利です。 また、印刷したい場合は、printingライブラリを使いましょう。 まずはpubspec.yamlに以下を追加しましょう。

dependencies:
  pdf: ^3.10.4
  printing: ^5.11.0

PDFファイルの生成方法

最低限のコードでPDFファイルを生成できるか試しましょう。

以下のコードをmain.dartに貼り付けて、flutter run -d chromeでアプリを起動しましょう。 右下のFABを押すと、印刷プレビュー画面が開くと思います。現時点では空のContainerしか中身が無いので真っ白のPDFファイルが表示されます。

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('PDFデモ'),
        ),
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.print),
          tooltip: '印刷',
          onPressed: () {
            // 印刷プレビューを表示する
            Printing.layoutPdf(
              onLayout: (_) {
                // PDFファイルを生成する
                return buildPdf();
              },
            );
          },
        ),
        body: Center(
          child: Text('右下のボタンを押してPDFファイルを生成'),
        ),
      ),
    );
  }

  // PDFファイルを生成
  Future<Uint8List> buildPdf() async {
    final pw.Document doc = pw.Document();

    doc.addPage(
      pw.MultiPage(
        pageFormat: PdfPageFormat.a4.copyWith(
          marginTop: 10,
          marginBottom: 10,
          marginLeft: 20,
          marginRight: 20,
        ),
        build: (pw.Context context) {
          return [
            // ここにPDFの中身を作る
            pw.Container(),
          ];
        },
      ),
    );

    return await doc.save();
  }
}

PDF印刷プレビュー(空白)

Widgetの作り方

pdfファイルの中身の実装では、Flutterの標準Widgetを使うことはできません。 代わりにpackage:pdf/widgets.dartWidgetを使用します。 ただし、Widget名はFlutterの標準Widgetと同じため、名前の衝突が起きるので、as pwと付けてimportしましょう。

import 'package:pdf/widgets.dart' as pw;

通常のFlutterのWidget(Containerなど)の先頭に、pwを付けて呼ぶとPDF用のWidgetを使用できます

pw.Column(
  children: [
    pw.Container(
      child: pw.Text('test')
    ),
  ]
)

PDFのサイズ指定と余白の指定

PDFファイルのサイズを変更するには、pw.MultiPage()のpageFormatにPdfPageFormat.a4にように指定するとA4サイズのPDFになります。

また、marginTop, marginBottom, marginLeft, marginRightで余白の調整が可能です。

PdfPageFormat.a4.copyWith(
  marginTop: 10,
  marginBottom: 10,
  marginLeft: 20,
  marginRight: 20,
);

表(Table)の作り方

pw.TableHelper.fromTextArray()を使用するとFlutterの標準WidgetのTableと同じような感覚でTableを作成できます。

以下のWidgetmain.dart内に定義して、pw.MultiPage()から呼んでみましょう。

アプリを実行して、印刷ボタンを押すとテーブルが表示されていると思います。

また、1つのセルの高さが高すぎる場合に、セル内で改ページしてしまうのではないかと心配になるかと思います。安心してください、いい感じに改ページしてくれます。

テーブルの細かい設定はたくさんあるためここでは割愛します。

pw.Widget _table() {
  // ヘッダー行
  final tableHeaders = [
    pw.Text('name'),
    pw.Text('height'),
    pw.Text('weight'),
  ];

  // データ
  final data = [
    [
      pw.Text('Eren'),
      pw.Text('170cm'),
      pw.Text('63kg'),
    ],
    [
      pw.Text('Armin'),
      pw.Text('163cm'),
      pw.Text('55kg'),
    ],
    [
      pw.Text('Levi'),
      pw.Text('160cm'),
      pw.Text('65kg'),
    ],
  ];

  return pw.TableHelper.fromTextArray(
    headers: List<pw.Widget>.generate(
      tableHeaders.length,
      (col) => tableHeaders[col],
    ),
    data: List<List<pw.Widget>>.generate(
      data.length,
      (row) => List<pw.Widget>.generate(
        tableHeaders.length,
        (col) => data[row][col],
      ),
    ),
  );
}

PDF印刷プレビュー(テーブル)

フォントの指定の仕方

フォントを指定することもできます。以下のようにttfファイルを読み込んで使用する必要があります。(otfファイルには対応していません)

final regularFont = rootBundle.load(
  'assets/fonts/NotoSansJPTTF/NotoSansJP-Regular.ttf',
);

pw.Text('test', style: pw.TextStyle(font: regularFont))

最大ページ数に制限がある

PDFファイルのページ数が多くなると、エラーになることがありますが、実はpw.MultiPage()は初期状態では20ページまでしか生成できません。

20ページ以上を生成した場合は、maxPagesに数値を指定しましょう。最大ページ数に制限を設けている理由はわかりませんが、おそらくパフォーマンス的な事だろうと思います。

pw.MultiPage(
  maxPages: 50
)

まとめ

  • pdfライブラリを使用するとFlutterで画面を作るようにPDFを生成できる

最後に

ウーオでは水産流通を革新するため、プロダクトを通じてあらゆるアプローチをしています。ウーオの事業やプロダクト開発にご興味がある方は、以下をぜひご覧ください 👇

uuuo.co.jp