坂本  篤司

坂本 篤司

1651899688

FlutterでPDFを作成する方法

ドキュメントの共有に関しては、PDFを使用するよりも良い方法はありません。もともとドキュメントがどこで開かれたとしても同じように見える方法として開発されたPDFは、今日、世界中のほぼすべての企業で使用されています。

PDFを使用してユーザーが読み取り可能なデータを送信することは、多くの理由から適切な選択です。たとえば、PDFを開くデバイスに関係なく、ドキュメントは同じように表示されます。また、ファイルサイズの点では、PDFは比較的小さいです。

PDFのもう1つの便利な機能は、誰でもこのファイルタイプをいつでも開くことができることです。AndroidやiOSなどの主要なOSは、この機能をすぐに提供します。

このチュートリアルでは、以下を確認します。

  • PDFを生成するFlutterアプリのセットアップ
  • FlutterでPDFに要素を追加する
  • FlutterでPDFプレビューページを作成する
  • 完成品の外観

PDFを生成するFlutterアプリのセットアップ

FlutterアプリケーションからPDFを作成することは、3つの理由から実際には非常に楽しい経験です。

まず、 pub.devには、適切に呼ばれるpdf、成熟した十分にテストされたライブラリがあります。

次に、Flutter PDFライブラリは、FlutterがUI内でウィジェットをレイアウトする方法とよく似たPDF要素をレイアウトします。行と列がどのように機能するかをすでに知っている場合は、この知識を再利用して、FlutterでPDFを作成および編集できます。

第三に、と呼ばれるコンパニオンパッケージprintingを使用すると、アプリ内からPDFを簡単にプレビュー、共有、および印刷できます。

Flutter内でPDFを作成する方法の例として、顧客の請求書を作成できるアプリの作成について説明します。このサンプルアプリでは、新しい広告申込情報を指定して、支払われるべき合計金額を計算することもできます。

請求書を作成したら、PDFに変換してお客様に送信できるようになります。Flutterアプリ内からこれを実現する方法を見てみましょう!

構成pubspec.yaml

まず、pubspecファイルに2つの適切なパッケージを追加する必要があります。

  • PDFpdf制作用パッケージ
  • printing作成したPDFをプレビューするためのパッケージ

これらの2つのパッケージを使用して、作成したPDFを作成して共有します。

次のように、を追加pdfprintingます。pubspec.yaml

dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  pdf: ## add this
  printing: ## also add this

請求書のモデルを設定する

次に、これらの請求書を作成して保存できるデータモデルを作成する必要があります。請求書には、関連する顧客情報が含まれ、請求されるラインアイテムのリストが表示され、これらのアイテムのコストが合計されている必要があります。

これらの要素を実現するために、次のようにデータモデルを作成しましょう。

class Invoice {
  final String customer;
  final String address;
  final List<LineItem> items;
  Invoice(this.customer, this.address, this.items);
  double totalCost() {
    return items.fold(0, (previousValue, element) => previousValue + element.cost);
  }
}

class LineItem {
  final String description;
  final double cost;

  LineItem(this.description, this.cost);
}

これは、請求書のデータを保持する非常に単純なデータクラスです。

この請求書に関連付けられているすべてのラインアイテムの合計コストを計算するために演算子をtotalCost使用する関数も宣言していることに気付いたかもしれません。.foldこの便利な関数がこの計算を処理するので、各値を手動で追加する必要はありません。

UIでの作業:請求書リストページ

アプリが起動すると、請求書のリストが表示されます。いくつかのテストデータをサンプリングして、最初に開いたときにリストにいくつかの項目が表示されるようにします。

まず、先に進んで、という名前の新しいフォルダを作成しましょうpages。そのフォルダ内に、という名前のDartファイルを作成しますinvoices.dartStatelessWidgetまた、この請求書のリストを最初に表示するためのを作成します。

このクラスでは、請求書自体のサンプルデータも宣言します。実際には、APIまたは同等のデータからこのデータをクエリする可能性がありますが、この場合、FlutterアプリでPDFを生成する方法を示すにはサンプルデータで十分です。

請求書ごとに、サンプルデータには次のものが含まれている必要があります。

  • お客様の名前と住所
  • 請求書の名前
  • それぞれの名前と費用で顧客に提供されるサービスの項目別リスト
final invoices = [
  Invoice(
      customer: 'David Thomas',
      address: '123 Fake St\r\nBermuda Triangle',
      items: [
        LineItem(
          'Technical Engagement',
          120,
        ),
        LineItem('Deployment Assistance', 200),
        LineItem('Develop Software Solution', 3020.45),
        LineItem('Produce Documentation', 840.50),
      ],
      name: 'Create and deploy software package'),
  Invoice(
    customer: 'Michael Ambiguous',
    address: '82 Unsure St\r\nBaggle Palace',
    items: [
      LineItem('Professional Advice', 100),
      LineItem('Lunch Bill', 43.55),
      LineItem('Remote Assistance', 50),
    ],
    name: 'Provide remote support after lunch',
  ),
  Invoice(
    customer: 'Marty McDanceFace',
    address: '55 Dancing Parade\r\nDance Place',
    items: [
      LineItem('Program the robots', 400.50),
      LineItem('Find tasteful dance moves for the robots', 80.55),
      LineItem('General quality assurance', 80),
    ],
    name: 'Create software to teach robots how to dance',
  )
];

クラス内InvoicePageでは、リスト内の既存のすべての請求書を表示するための非常にシンプルなUIも設計します。このリストの各アイテムには、請求書の名前、顧客の名前、合計費用など、請求書の詳細のプレビューが表示されます。

ListViewこれは、ウィジェットをListTile次のように任意のアイテムと組み合わせることによって行われます。

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Invoices'),
    ),
    body: ListView(
      children: [
        ...invoices.map(
          (e) => ListTile(
            title: Text(e.name),
            subtitle: Text(e.customer),
            trailing: Text('\$${e.totalCost().toStringAsFixed(2)}'),
            onTap: () {
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (builder) => DetailPage(invoice: e),
                ),
              );
            },
          ),
        )
      ],
    ),
  );
}

mapリストの演算子を使用してinvoices、リストをListTileアイテムに変換します。これは、に表示できますListViewtrailingまた、次の方法を使用して、表示される請求書の合計コストを設定します。

trailing: Text('\$${e.totalCost().toStringAsFixed(2)}'),

この文字列補間方法は、少し混乱する可能性があります。それをよりよく理解するためにそれを分解しましょう。

\$文字列内でドル記号としてレンダリングされます。通常、文字列補間を示す\ために使用されるため、接頭辞としてaを付ける必要があります。$この場合、実際には生のドル記号自体を使用したいので、を使用して通常の使用法を回避する必要があり\ます。

の接頭辞なしの使用法は、請求書$の関数の文字列補間を開始します。totalCost最後に、数値を文字列に変換するときに、小数点以下2桁に切り捨てます。

ウィジェットは、次のようにすべての請求書のリストを生成します。

各請求書をクリックすると、アプリはに移動しますDetailPage。ここで、サンプルの詳細ページを作成する方法を見てみましょう。

UIでの作業:請求書の詳細ページ

DetailPage請求書をパラメーターとして受け入れ、請求書オブジェクトを、PDFを作成する前にFlutterアプリでユーザーが確認できるものに変換します。

ここでも、とを使用しScaffoldListView請求書の詳細を表示します。またFloatingActionButton、Flutter独自のウィジェットであるを使用して、ユーザーが請求書情報を含むPDFを作成および共有できるようにします。

これらはFlutterで知っておくべき優れたUI要素ですが、これを生成するために使用するコードに焦点を当て続けましょうDetailPage。これは次のようになります。

class DetailPage extends StatelessWidget {
  final Invoice invoice;
  const DetailPage({
    Key? key,
    required this.invoice,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.of(context).push(
            MaterialPageRoute(
              builder: (context) => PdfPreviewPage(invoice: invoice),
            ),
          );
          // rootBundle.
        },
        child: Icon(Icons.picture_as_pdf),
      ),
      appBar: AppBar(
        title: Text(invoice.name),
      ),
      body: ListView(
        children: [
          Padding(
            padding: const EdgeInsets.all(15.0),
            child: Card(
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Expanded(
                    child: Text(
                      'Customer',
                      style: Theme.of(context).textTheme.headline5,
                    ),
                  ),
                  Expanded(
                    child: Text(
                      invoice.customer,
                      style: Theme.of(context).textTheme.headline4,
                      textAlign: TextAlign.center,
                    ),
                  ),
                ],
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(15.0),
            child: Card(
              child: Column(
                children: [
                  Text(
                    'Invoice Items',
                    style: Theme.of(context).textTheme.headline6,
                  ),
                  ...invoice.items.map(
                    (e) => ListTile(
                      title: Text(e.description),
                      trailing: Text(
                        e.cost.toStringAsFixed(2),
                      ),
                    ),
                  ),
                  DefaultTextStyle.merge(
                    style: Theme.of(context).textTheme.headline4,
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceAround,
                      children: [
                        Text("Total"),
                        Text(
                          invoice.totalCost().toStringAsFixed(2),
                        ),
                      ],
                    ),
                  )
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

このコードにより、次のような請求書プレビューページが表示されます。

FlutterでPDFに要素を追加する

請求書アプリのPDFを作成するには、最初に完成品がどのように見えるかを理解する必要があります。ほとんどの請求書には次のものが含まれます。

  • 顧客に関する情報
  • 会社のロゴ
  • 提供されたサービスのリスト
  • 最終価格(GSTを含む)
  • 支払いの詳細、または会社が請求書を処理するために必要な情報

これを作成するには、PDFに非常に複雑な視覚的レイアウトが必要です。PDFの請求書には、写真、テキスト、表、およびその線より下のすべてが買掛金部門のものであることを示す点線が含まれている必要があります。

通常、オフセットを使用する必要があり、実際には、すべてが必要な場所に正確にピクセルで表現しようとします。ただし、このpdfパッケージの主な利点の1つは、 Flutterと同じレイアウトルールを使用してPDFを作成できることです。

写真の作成ColumnsRows読み込み、パディングの設定方法をすでに知っている場合は、PDFのレイアウト方法も知っている必要があります。これにより、Flutterアプリケーション内から独自のPDFを作成および作成する際の障壁がすぐに下がります。

PDFを作成するために、という名前の新しいDartファイルを作成しますpdfexport。このクラスは、作成しているPDFのバイナリデータを返す単一の関数を公開します。

Dartファイルで関数を宣言し、makePdfタイプがパラメーターを受け入れるようにしますInvoiceDocument次に、オブジェクトを宣言し、ページを追加し、ページにを追加して、PDFドキュメントのシェルを構築しColumnます。

Future<Uint8List> makePdf(Invoice invoice) async {
  final pdf = Document();
  pdf.addPage(
    Page(
    build: (context) {
      return Column(
        children: []
      }
    );
}

必要に応じて、このページに個々の情報を追加します。PDFには、顧客の詳細、コストの内訳、買掛金に渡される伝票の3つの主要な領域が必要です。

終了すると、PDFは次のようになります。

アドレスとロゴの行を作成する

請求書の最初の行は、顧客情報とロゴの行です。当社のロゴが含まれているため、当社のロゴへの参照を追加しますpubspec.yaml。私の場合、単純なロゴを生成しただけですが、任意のPNG画像を使用できます。

assets:
   - assets/technical_logo.png

関数内に戻ってmakePdf、PDFに表示するアセットからこのPNGをロードする必要があります。幸い、これは、この特定の画像を読み込んでメモリに保存することをFlutterに伝えるのと同じくらい簡単です。

final imageLogo = MemoryImage((await rootBundle.load('assets/technical_logo.png')).buffer.asUint8List());

これで、顧客の詳細と会社のロゴを含む最初の行を作成できます。

Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: [
    Column(
      children: [
        Text("Attention to: ${invoice.customer}"),
        Text(invoice.address),
      ],
      crossAxisAlignment: CrossAxisAlignment.start,
    ),
    SizedBox(
      height: 150,
      width: 150,
      child: Image(imageLogo),
    )
  ],
),

を使用して、この行の両方の子を、使用可能なスペースが許す限り互いに離れるように配置しますMainAxisAlignment.spaceBetween。次に、最初の顧客の詳細を指定し、Columnこの子をColumn左側に配置します。

Image次に、内にロードしSizedBox、サイズと高さを150に制限して、会社のロゴがあまりスペースをとらないようにします。この行の結果は次のようになります。

うまくいけば、私たちは一般的に利用可能な構造をどのように使用するかを理解し始め、RowColumnたちが好きな方法でPDFをレイアウトするのを非常に簡単にすることができます。

次に、請求書の詳細を含むテーブルを作成しましょう。

請求書テーブルの作成

請求書テーブルには、請求対象の商品またはサービスの項目別リストが表示されます。また、各アイテムの個別のコストも表示する必要があります。

適切な間隔でテーブルにアイテムを表示すると、請求書の特定のラインアイテムに関連付けられているコストを簡単に確認できます。これを支援するために、オブジェクトPaddedTextの周囲に必要なパディングの種類を指定するために呼び出される単純なヘルパークラスを追加しましょう。Text

Widget PaddedText(
  final String text, {
  final TextAlign align = TextAlign.left,
}) =>
    Padding(
      padding: EdgeInsets.all(10),
      child: Text(
        text,
        textAlign: align,
      ),
    );

パッケージTable内でこの機能を実現するために使用できます。pdfこれにより、PDF内に表示するための適切な黒い境界線を持つテーブルを設定できます。

この特定の行のレイアウトはもう少し複雑なので、これがどのように達成されるかを理解するために、以下のインラインコメントを参照できます。

Table(
  border: TableBorder.all(color: PdfColors.black),
  children: [
   // The first row just contains a phrase 'INVOICE FOR PAYMENT'
    TableRow(
      children: [
        Padding(
          child: Text(
            'INVOICE FOR PAYMENT',
            style: Theme.of(context).header4,
            textAlign: TextAlign.center,
          ),
          padding: EdgeInsets.all(20),
        ),
      ],
    ),
    // The remaining rows contain each item from the invoice, and uses the
    // map operator (the ...) to include these items in the list
    ...invoice.items.map(
    // Each new line item for the invoice should be rendered on a new TableRow
      (e) => TableRow(
        children: [
          // We can use an Expanded widget, and use the flex parameter to specify
          // how wide this particular widget should be. With a flex parameter of
          // 2, the description widget will be 66% of the available width.
          Expanded(
            child: PaddedText(e.description),
            flex: 2,
          ),
          // Again, with a flex parameter of 1, the cost widget will be 33% of the
          // available width.
          Expanded(
            child: PaddedText("\$${e.cost}"),
            flex: 1,
          )
        ],
      ),
    ),
    // After the itemized breakdown of costs, show the tax amount for this invoice
    // In this case, it's just 10% of the invoice amount
    TableRow(
      children: [
        PaddedText('TAX', align: TextAlign.right),
        PaddedText('\$${(invoice.totalCost() * 0.1).toStringAsFixed(2)}'),
      ],
    ),
    // Show the total 
    TableRow(
      children: [
        PaddedText('TOTAL', align: TextAlign.right),
        PaddedText("\$${invoice.totalCost()}"),
      ],
    )
  ],
),
Padding(
  child: Text(
    "THANK YOU FOR YOUR BUSINESS!",
    style: Theme.of(context).header2,
  ),
  padding: EdgeInsets.all(20),
),

このコードの結果は、次のように、請求書に関連付けられた商品またはサービスとそれぞれのコストの項目別リストを示しています。

支払い伝票の作成

最後に、請求書の2番目の部分を買掛金部門に転送できることを示すために点線を含める必要があります。このPDF要素には、顧客が請求書を正しく支払うことができるように、支払いの詳細も表示する必要があります。

以下のコードは、PDFで点線を指定し、別のテーブルを使用してアカウント情報を表示する方法を示しています。最後に、この請求書を支払うときに小切手に含める情報について説明します。

繰り返しになりますが、これは長いコードであるため、インラインコメントを参照して、何が起こっているのかを理解してください。

Text("Please forward the below slip to your accounts payable department."),
// Create a divider that is 1 unit high and make the appearance of
// the line dashed
Divider(
  height: 1,
  borderStyle: BorderStyle.dashed,
),
// Space out the invoice appropriately
Container(height: 50),
// Create another table with the payment details
Table(
  border: TableBorder.all(color: PdfColors.black),
  children: [
    TableRow(
      children: [
        PaddedText('Account Number'),
        PaddedText(
          '1234 1234',
        )
      ],
    ),
    TableRow(
      children: [
        PaddedText(
          'Account Name',
        ),
        PaddedText(
          'ADAM FAMILY TRUST',
        )
      ],
    ),
    TableRow(
      children: [
        PaddedText(
          'Total Amount to be Paid',
        ),
        PaddedText('\$${(invoice.totalCost() * 1.1).toStringAsFixed(2)}')
      ],
    )
  ],
),
// Add a final instruction about how checks should be created
// Center align and italicize this text to draw the reader's attention
// to it.
Padding(
  padding: EdgeInsets.all(30),
  child: Text(
    'Please ensure all checks are payable to the ADAM FAMILY TRUST.',
    style: Theme.of(context).header3.copyWith(
          fontStyle: FontStyle.italic,
        ),
    textAlign: TextAlign.center,
  ),
)

最後に、makePdf関数の最後に、生成されたPDFを呼び出し元に返す必要があります。

return pdf.save();

PdfPreview最後に、ウィジェットを表示するための基本的なページを作成する必要があります。今それをしましょう。

FlutterでPDFプレビューページを作成する

printingパッケージを使用すると、PDFプレビューアの作成が簡単になります。Scaffold(ユーザーが引き続きアプリ内をナビゲートできるように)を含めてから、の本体をScaffoldとして指定する必要がありますPdfPreview

buildの関数内で、PdfPreviewPDFを作成する関数を呼び出します。このビルド関数はPDFのバイト配列を受け入れますが、PDFのバイト配列を生成するも受け入れFutureます。

これらのオプションを使用すると、PDFを生成するコードが非同期であっても、PDFを作成する関数を簡単に呼び出すことができます。

class PdfPreviewPage extends StatelessWidget {
  final Invoice invoice;
  const PdfPreviewPage({Key? key, required this.invoice}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('PDF Preview'),
      ),
      body: PdfPreview(
        build: (context) => makePdf(invoice),
      ),
    );
  }
}

完成品の外観

上記の結果は、指定したデータに基づいてPDFを生成するアプリです。PdfPreviewPageまた、PdfPreviewウィジェットには、PDFを電子メールまたは印刷してダウンロードおよび共有するためのオプションが含まれていることもわかります。

この記事の例では静的データを使用していますが、APIからこのデータを読み込んでPDFで表示するのはかなり簡単です。いつものように、GitHubからコードのコピーを取得できます。

この記事で、Flutter内からPDFを作成して共有する方法を説明できれば幸いです。Flutterレイアウトシステムについて既に理解している場合は、この知識を再利用して、アプリ内で美しく有益なPDFを作成できます。 

出典:https ://blog.logrocket.com/how-create-pdfs-flutter/

#flutter #pdf 

What is GEEK

Buddha Community

FlutterでPDFを作成する方法
坂本  篤司

坂本 篤司

1651899688

FlutterでPDFを作成する方法

ドキュメントの共有に関しては、PDFを使用するよりも良い方法はありません。もともとドキュメントがどこで開かれたとしても同じように見える方法として開発されたPDFは、今日、世界中のほぼすべての企業で使用されています。

PDFを使用してユーザーが読み取り可能なデータを送信することは、多くの理由から適切な選択です。たとえば、PDFを開くデバイスに関係なく、ドキュメントは同じように表示されます。また、ファイルサイズの点では、PDFは比較的小さいです。

PDFのもう1つの便利な機能は、誰でもこのファイルタイプをいつでも開くことができることです。AndroidやiOSなどの主要なOSは、この機能をすぐに提供します。

このチュートリアルでは、以下を確認します。

  • PDFを生成するFlutterアプリのセットアップ
  • FlutterでPDFに要素を追加する
  • FlutterでPDFプレビューページを作成する
  • 完成品の外観

PDFを生成するFlutterアプリのセットアップ

FlutterアプリケーションからPDFを作成することは、3つの理由から実際には非常に楽しい経験です。

まず、 pub.devには、適切に呼ばれるpdf、成熟した十分にテストされたライブラリがあります。

次に、Flutter PDFライブラリは、FlutterがUI内でウィジェットをレイアウトする方法とよく似たPDF要素をレイアウトします。行と列がどのように機能するかをすでに知っている場合は、この知識を再利用して、FlutterでPDFを作成および編集できます。

第三に、と呼ばれるコンパニオンパッケージprintingを使用すると、アプリ内からPDFを簡単にプレビュー、共有、および印刷できます。

Flutter内でPDFを作成する方法の例として、顧客の請求書を作成できるアプリの作成について説明します。このサンプルアプリでは、新しい広告申込情報を指定して、支払われるべき合計金額を計算することもできます。

請求書を作成したら、PDFに変換してお客様に送信できるようになります。Flutterアプリ内からこれを実現する方法を見てみましょう!

構成pubspec.yaml

まず、pubspecファイルに2つの適切なパッケージを追加する必要があります。

  • PDFpdf制作用パッケージ
  • printing作成したPDFをプレビューするためのパッケージ

これらの2つのパッケージを使用して、作成したPDFを作成して共有します。

次のように、を追加pdfprintingます。pubspec.yaml

dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  pdf: ## add this
  printing: ## also add this

請求書のモデルを設定する

次に、これらの請求書を作成して保存できるデータモデルを作成する必要があります。請求書には、関連する顧客情報が含まれ、請求されるラインアイテムのリストが表示され、これらのアイテムのコストが合計されている必要があります。

これらの要素を実現するために、次のようにデータモデルを作成しましょう。

class Invoice {
  final String customer;
  final String address;
  final List<LineItem> items;
  Invoice(this.customer, this.address, this.items);
  double totalCost() {
    return items.fold(0, (previousValue, element) => previousValue + element.cost);
  }
}

class LineItem {
  final String description;
  final double cost;

  LineItem(this.description, this.cost);
}

これは、請求書のデータを保持する非常に単純なデータクラスです。

この請求書に関連付けられているすべてのラインアイテムの合計コストを計算するために演算子をtotalCost使用する関数も宣言していることに気付いたかもしれません。.foldこの便利な関数がこの計算を処理するので、各値を手動で追加する必要はありません。

UIでの作業:請求書リストページ

アプリが起動すると、請求書のリストが表示されます。いくつかのテストデータをサンプリングして、最初に開いたときにリストにいくつかの項目が表示されるようにします。

まず、先に進んで、という名前の新しいフォルダを作成しましょうpages。そのフォルダ内に、という名前のDartファイルを作成しますinvoices.dartStatelessWidgetまた、この請求書のリストを最初に表示するためのを作成します。

このクラスでは、請求書自体のサンプルデータも宣言します。実際には、APIまたは同等のデータからこのデータをクエリする可能性がありますが、この場合、FlutterアプリでPDFを生成する方法を示すにはサンプルデータで十分です。

請求書ごとに、サンプルデータには次のものが含まれている必要があります。

  • お客様の名前と住所
  • 請求書の名前
  • それぞれの名前と費用で顧客に提供されるサービスの項目別リスト
final invoices = [
  Invoice(
      customer: 'David Thomas',
      address: '123 Fake St\r\nBermuda Triangle',
      items: [
        LineItem(
          'Technical Engagement',
          120,
        ),
        LineItem('Deployment Assistance', 200),
        LineItem('Develop Software Solution', 3020.45),
        LineItem('Produce Documentation', 840.50),
      ],
      name: 'Create and deploy software package'),
  Invoice(
    customer: 'Michael Ambiguous',
    address: '82 Unsure St\r\nBaggle Palace',
    items: [
      LineItem('Professional Advice', 100),
      LineItem('Lunch Bill', 43.55),
      LineItem('Remote Assistance', 50),
    ],
    name: 'Provide remote support after lunch',
  ),
  Invoice(
    customer: 'Marty McDanceFace',
    address: '55 Dancing Parade\r\nDance Place',
    items: [
      LineItem('Program the robots', 400.50),
      LineItem('Find tasteful dance moves for the robots', 80.55),
      LineItem('General quality assurance', 80),
    ],
    name: 'Create software to teach robots how to dance',
  )
];

クラス内InvoicePageでは、リスト内の既存のすべての請求書を表示するための非常にシンプルなUIも設計します。このリストの各アイテムには、請求書の名前、顧客の名前、合計費用など、請求書の詳細のプレビューが表示されます。

ListViewこれは、ウィジェットをListTile次のように任意のアイテムと組み合わせることによって行われます。

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Invoices'),
    ),
    body: ListView(
      children: [
        ...invoices.map(
          (e) => ListTile(
            title: Text(e.name),
            subtitle: Text(e.customer),
            trailing: Text('\$${e.totalCost().toStringAsFixed(2)}'),
            onTap: () {
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (builder) => DetailPage(invoice: e),
                ),
              );
            },
          ),
        )
      ],
    ),
  );
}

mapリストの演算子を使用してinvoices、リストをListTileアイテムに変換します。これは、に表示できますListViewtrailingまた、次の方法を使用して、表示される請求書の合計コストを設定します。

trailing: Text('\$${e.totalCost().toStringAsFixed(2)}'),

この文字列補間方法は、少し混乱する可能性があります。それをよりよく理解するためにそれを分解しましょう。

\$文字列内でドル記号としてレンダリングされます。通常、文字列補間を示す\ために使用されるため、接頭辞としてaを付ける必要があります。$この場合、実際には生のドル記号自体を使用したいので、を使用して通常の使用法を回避する必要があり\ます。

の接頭辞なしの使用法は、請求書$の関数の文字列補間を開始します。totalCost最後に、数値を文字列に変換するときに、小数点以下2桁に切り捨てます。

ウィジェットは、次のようにすべての請求書のリストを生成します。

各請求書をクリックすると、アプリはに移動しますDetailPage。ここで、サンプルの詳細ページを作成する方法を見てみましょう。

UIでの作業:請求書の詳細ページ

DetailPage請求書をパラメーターとして受け入れ、請求書オブジェクトを、PDFを作成する前にFlutterアプリでユーザーが確認できるものに変換します。

ここでも、とを使用しScaffoldListView請求書の詳細を表示します。またFloatingActionButton、Flutter独自のウィジェットであるを使用して、ユーザーが請求書情報を含むPDFを作成および共有できるようにします。

これらはFlutterで知っておくべき優れたUI要素ですが、これを生成するために使用するコードに焦点を当て続けましょうDetailPage。これは次のようになります。

class DetailPage extends StatelessWidget {
  final Invoice invoice;
  const DetailPage({
    Key? key,
    required this.invoice,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.of(context).push(
            MaterialPageRoute(
              builder: (context) => PdfPreviewPage(invoice: invoice),
            ),
          );
          // rootBundle.
        },
        child: Icon(Icons.picture_as_pdf),
      ),
      appBar: AppBar(
        title: Text(invoice.name),
      ),
      body: ListView(
        children: [
          Padding(
            padding: const EdgeInsets.all(15.0),
            child: Card(
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Expanded(
                    child: Text(
                      'Customer',
                      style: Theme.of(context).textTheme.headline5,
                    ),
                  ),
                  Expanded(
                    child: Text(
                      invoice.customer,
                      style: Theme.of(context).textTheme.headline4,
                      textAlign: TextAlign.center,
                    ),
                  ),
                ],
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(15.0),
            child: Card(
              child: Column(
                children: [
                  Text(
                    'Invoice Items',
                    style: Theme.of(context).textTheme.headline6,
                  ),
                  ...invoice.items.map(
                    (e) => ListTile(
                      title: Text(e.description),
                      trailing: Text(
                        e.cost.toStringAsFixed(2),
                      ),
                    ),
                  ),
                  DefaultTextStyle.merge(
                    style: Theme.of(context).textTheme.headline4,
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceAround,
                      children: [
                        Text("Total"),
                        Text(
                          invoice.totalCost().toStringAsFixed(2),
                        ),
                      ],
                    ),
                  )
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

このコードにより、次のような請求書プレビューページが表示されます。

FlutterでPDFに要素を追加する

請求書アプリのPDFを作成するには、最初に完成品がどのように見えるかを理解する必要があります。ほとんどの請求書には次のものが含まれます。

  • 顧客に関する情報
  • 会社のロゴ
  • 提供されたサービスのリスト
  • 最終価格(GSTを含む)
  • 支払いの詳細、または会社が請求書を処理するために必要な情報

これを作成するには、PDFに非常に複雑な視覚的レイアウトが必要です。PDFの請求書には、写真、テキスト、表、およびその線より下のすべてが買掛金部門のものであることを示す点線が含まれている必要があります。

通常、オフセットを使用する必要があり、実際には、すべてが必要な場所に正確にピクセルで表現しようとします。ただし、このpdfパッケージの主な利点の1つは、 Flutterと同じレイアウトルールを使用してPDFを作成できることです。

写真の作成ColumnsRows読み込み、パディングの設定方法をすでに知っている場合は、PDFのレイアウト方法も知っている必要があります。これにより、Flutterアプリケーション内から独自のPDFを作成および作成する際の障壁がすぐに下がります。

PDFを作成するために、という名前の新しいDartファイルを作成しますpdfexport。このクラスは、作成しているPDFのバイナリデータを返す単一の関数を公開します。

Dartファイルで関数を宣言し、makePdfタイプがパラメーターを受け入れるようにしますInvoiceDocument次に、オブジェクトを宣言し、ページを追加し、ページにを追加して、PDFドキュメントのシェルを構築しColumnます。

Future<Uint8List> makePdf(Invoice invoice) async {
  final pdf = Document();
  pdf.addPage(
    Page(
    build: (context) {
      return Column(
        children: []
      }
    );
}

必要に応じて、このページに個々の情報を追加します。PDFには、顧客の詳細、コストの内訳、買掛金に渡される伝票の3つの主要な領域が必要です。

終了すると、PDFは次のようになります。

アドレスとロゴの行を作成する

請求書の最初の行は、顧客情報とロゴの行です。当社のロゴが含まれているため、当社のロゴへの参照を追加しますpubspec.yaml。私の場合、単純なロゴを生成しただけですが、任意のPNG画像を使用できます。

assets:
   - assets/technical_logo.png

関数内に戻ってmakePdf、PDFに表示するアセットからこのPNGをロードする必要があります。幸い、これは、この特定の画像を読み込んでメモリに保存することをFlutterに伝えるのと同じくらい簡単です。

final imageLogo = MemoryImage((await rootBundle.load('assets/technical_logo.png')).buffer.asUint8List());

これで、顧客の詳細と会社のロゴを含む最初の行を作成できます。

Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: [
    Column(
      children: [
        Text("Attention to: ${invoice.customer}"),
        Text(invoice.address),
      ],
      crossAxisAlignment: CrossAxisAlignment.start,
    ),
    SizedBox(
      height: 150,
      width: 150,
      child: Image(imageLogo),
    )
  ],
),

を使用して、この行の両方の子を、使用可能なスペースが許す限り互いに離れるように配置しますMainAxisAlignment.spaceBetween。次に、最初の顧客の詳細を指定し、Columnこの子をColumn左側に配置します。

Image次に、内にロードしSizedBox、サイズと高さを150に制限して、会社のロゴがあまりスペースをとらないようにします。この行の結果は次のようになります。

うまくいけば、私たちは一般的に利用可能な構造をどのように使用するかを理解し始め、RowColumnたちが好きな方法でPDFをレイアウトするのを非常に簡単にすることができます。

次に、請求書の詳細を含むテーブルを作成しましょう。

請求書テーブルの作成

請求書テーブルには、請求対象の商品またはサービスの項目別リストが表示されます。また、各アイテムの個別のコストも表示する必要があります。

適切な間隔でテーブルにアイテムを表示すると、請求書の特定のラインアイテムに関連付けられているコストを簡単に確認できます。これを支援するために、オブジェクトPaddedTextの周囲に必要なパディングの種類を指定するために呼び出される単純なヘルパークラスを追加しましょう。Text

Widget PaddedText(
  final String text, {
  final TextAlign align = TextAlign.left,
}) =>
    Padding(
      padding: EdgeInsets.all(10),
      child: Text(
        text,
        textAlign: align,
      ),
    );

パッケージTable内でこの機能を実現するために使用できます。pdfこれにより、PDF内に表示するための適切な黒い境界線を持つテーブルを設定できます。

この特定の行のレイアウトはもう少し複雑なので、これがどのように達成されるかを理解するために、以下のインラインコメントを参照できます。

Table(
  border: TableBorder.all(color: PdfColors.black),
  children: [
   // The first row just contains a phrase 'INVOICE FOR PAYMENT'
    TableRow(
      children: [
        Padding(
          child: Text(
            'INVOICE FOR PAYMENT',
            style: Theme.of(context).header4,
            textAlign: TextAlign.center,
          ),
          padding: EdgeInsets.all(20),
        ),
      ],
    ),
    // The remaining rows contain each item from the invoice, and uses the
    // map operator (the ...) to include these items in the list
    ...invoice.items.map(
    // Each new line item for the invoice should be rendered on a new TableRow
      (e) => TableRow(
        children: [
          // We can use an Expanded widget, and use the flex parameter to specify
          // how wide this particular widget should be. With a flex parameter of
          // 2, the description widget will be 66% of the available width.
          Expanded(
            child: PaddedText(e.description),
            flex: 2,
          ),
          // Again, with a flex parameter of 1, the cost widget will be 33% of the
          // available width.
          Expanded(
            child: PaddedText("\$${e.cost}"),
            flex: 1,
          )
        ],
      ),
    ),
    // After the itemized breakdown of costs, show the tax amount for this invoice
    // In this case, it's just 10% of the invoice amount
    TableRow(
      children: [
        PaddedText('TAX', align: TextAlign.right),
        PaddedText('\$${(invoice.totalCost() * 0.1).toStringAsFixed(2)}'),
      ],
    ),
    // Show the total 
    TableRow(
      children: [
        PaddedText('TOTAL', align: TextAlign.right),
        PaddedText("\$${invoice.totalCost()}"),
      ],
    )
  ],
),
Padding(
  child: Text(
    "THANK YOU FOR YOUR BUSINESS!",
    style: Theme.of(context).header2,
  ),
  padding: EdgeInsets.all(20),
),

このコードの結果は、次のように、請求書に関連付けられた商品またはサービスとそれぞれのコストの項目別リストを示しています。

支払い伝票の作成

最後に、請求書の2番目の部分を買掛金部門に転送できることを示すために点線を含める必要があります。このPDF要素には、顧客が請求書を正しく支払うことができるように、支払いの詳細も表示する必要があります。

以下のコードは、PDFで点線を指定し、別のテーブルを使用してアカウント情報を表示する方法を示しています。最後に、この請求書を支払うときに小切手に含める情報について説明します。

繰り返しになりますが、これは長いコードであるため、インラインコメントを参照して、何が起こっているのかを理解してください。

Text("Please forward the below slip to your accounts payable department."),
// Create a divider that is 1 unit high and make the appearance of
// the line dashed
Divider(
  height: 1,
  borderStyle: BorderStyle.dashed,
),
// Space out the invoice appropriately
Container(height: 50),
// Create another table with the payment details
Table(
  border: TableBorder.all(color: PdfColors.black),
  children: [
    TableRow(
      children: [
        PaddedText('Account Number'),
        PaddedText(
          '1234 1234',
        )
      ],
    ),
    TableRow(
      children: [
        PaddedText(
          'Account Name',
        ),
        PaddedText(
          'ADAM FAMILY TRUST',
        )
      ],
    ),
    TableRow(
      children: [
        PaddedText(
          'Total Amount to be Paid',
        ),
        PaddedText('\$${(invoice.totalCost() * 1.1).toStringAsFixed(2)}')
      ],
    )
  ],
),
// Add a final instruction about how checks should be created
// Center align and italicize this text to draw the reader's attention
// to it.
Padding(
  padding: EdgeInsets.all(30),
  child: Text(
    'Please ensure all checks are payable to the ADAM FAMILY TRUST.',
    style: Theme.of(context).header3.copyWith(
          fontStyle: FontStyle.italic,
        ),
    textAlign: TextAlign.center,
  ),
)

最後に、makePdf関数の最後に、生成されたPDFを呼び出し元に返す必要があります。

return pdf.save();

PdfPreview最後に、ウィジェットを表示するための基本的なページを作成する必要があります。今それをしましょう。

FlutterでPDFプレビューページを作成する

printingパッケージを使用すると、PDFプレビューアの作成が簡単になります。Scaffold(ユーザーが引き続きアプリ内をナビゲートできるように)を含めてから、の本体をScaffoldとして指定する必要がありますPdfPreview

buildの関数内で、PdfPreviewPDFを作成する関数を呼び出します。このビルド関数はPDFのバイト配列を受け入れますが、PDFのバイト配列を生成するも受け入れFutureます。

これらのオプションを使用すると、PDFを生成するコードが非同期であっても、PDFを作成する関数を簡単に呼び出すことができます。

class PdfPreviewPage extends StatelessWidget {
  final Invoice invoice;
  const PdfPreviewPage({Key? key, required this.invoice}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('PDF Preview'),
      ),
      body: PdfPreview(
        build: (context) => makePdf(invoice),
      ),
    );
  }
}

完成品の外観

上記の結果は、指定したデータに基づいてPDFを生成するアプリです。PdfPreviewPageまた、PdfPreviewウィジェットには、PDFを電子メールまたは印刷してダウンロードおよび共有するためのオプションが含まれていることもわかります。

この記事の例では静的データを使用していますが、APIからこのデータを読み込んでPDFで表示するのはかなり簡単です。いつものように、GitHubからコードのコピーを取得できます。

この記事で、Flutter内からPDFを作成して共有する方法を説明できれば幸いです。Flutterレイアウトシステムについて既に理解している場合は、この知識を再利用して、アプリ内で美しく有益なPDFを作成できます。 

出典:https ://blog.logrocket.com/how-create-pdfs-flutter/

#flutter #pdf