Create dynamic table rows in PDF document using the pdf package
Asked Answered
G

2

7

Background My Flutter app uses the pdf/pdf.dart package to create a PDF document which displays information from a Firestore database collection. The PDF document contains a table. I would like to dynamically and programatically create one row in the table for each record in that Firestore collection.

Current State I have been able to create the PDF document and a static table with a pre-set number of rows. However, I cannot dynamically and programatically create one row in the table for each record in the Firestore collection.

Question How can I dynamically and programatically create one row in the table for each record in a nominated Firestore collection?

Code Snippet: The Table

pw.Table(border: pw.TableBorder(), children: [
            pw.TableRow(
              children: [
                paddedHeadingTextCell('Product'),
                paddedHeadingTextCell('Unit'),
                paddedHeadingTextCell('Qty'),
                paddedHeadingTextCell('Price'),
                paddedHeadingTextCell('Discount'),
                paddedHeadingTextCell('Tax'),
                paddedHeadingTextCell('Total AUD'),
              ],
            ),
            // Now the next table row
            buildRowsForInventoryItems(),  // This triggers the Row Builder, which does not work.
          ])

Code Snippit: The Non-functional Row builder

pw.TableRow buildRowsForInventoryItems() {
  List<pw.Column> itemList = [];

  var items = DatabaseService().returnAllInventoryItemsFromAPurchaseOrder();
  for (var item in items) {
    final description = item.data()['description'].toString();
    final unit = item.data()['unit'].toString();
    final qty = item.data()['qty'].toString();
    itemList.add(
      pw.Column(
        children: [
          pw.Text(description),
        ],
      ),
    );
    itemList.add(
      pw.Column(
        children: [
          pw.Text(unit),
        ],
      ),
    );
    itemList.add(
      pw.Column(
        children: [
          pw.Text(qty),
        ],
      ),
    );
  }
  return pw.TableRow(children: itemList);
}

Complete Code

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:invento/constants.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:universal_html/html.dart' as html;
import 'package:invento/Services/database_service.dart';

class reporting extends StatefulWidget {
  @override
  _reportingState createState() => _reportingState();
}

final pdf = pw.Document();
String orderNumber = '<<Order_Number>>';
String disclaimerText = '<<Disclaimer_Text>>';
String supplierEmailAddress = '<<Supplier_Name>>';
String supplierName = '<<Supplier_Email_Address>>';
String orderDate = '<<Order_Date>>';
String requiredByDate = '<<Required_By_Date>>';
String ourShipmentAddress = '<<Our_Shipment_Address>>';
String notes = '<<Notes>>';

void createPdfDocument(pw.Document pdf) {
  pdf.addPage(pw.MultiPage(
      pageFormat: PdfPageFormat.a4,
      margin: pw.EdgeInsets.all(31), // This is the page margin
      build: (pw.Context context) {
        return <pw.Widget>[
// Document heading with logo, disclaimer text, and title
          pw.Table(
            children: [
              pw.TableRow(
                children: [
                  pw.Column(
                    crossAxisAlignment: pw.CrossAxisAlignment.start,
                    children: [
                      //
                      pw.Placeholder(
                          fallbackHeight: 50,
                          fallbackWidth: 75,
                          strokeWidth: 2),
                      pw.Text(
                        '$disclaimerText',
                        style: pw.TextStyle(fontSize: 8, color: PdfColors.grey),
                      ),
                    ],
                  ),
                  pw.Column(
                    mainAxisAlignment: pw.MainAxisAlignment.center,
                    crossAxisAlignment: pw.CrossAxisAlignment.end,
                    children: [
                      pw.Text(
                        'Purchase Order',
                        style: pw.TextStyle(
                            fontSize: 28, color: PdfColors.deepOrange400),
                      ),
                      pw.Text('Reference Number $orderNumber',
                          textAlign: pw.TextAlign.right),
                    ],
                  ),
                ],
              ),
            ],
            //
          ),
// Gap
          pw.Divider(color: PdfColors.white, thickness: 10),

// Supplier Details
          pw.Table(// This is the starting widget for the table
              children: [
            pw.TableRow(
              // This is the starting row for the table, within which the subsequent columns will be nested
              children: [
                pw.Padding(
                  padding: pw.EdgeInsets.all(4),
                  child: pw.Column(
                    crossAxisAlignment: pw.CrossAxisAlignment.start,
                    children: [
                      pw.Text(
                        'Purchase Order To',
                        style: pwTableHeadingTextStyle(),
                      ),
                      pw.Text(supplierName),
                      pw.Text(supplierEmailAddress),
                    ],
                  ),
                ),
                pw.Padding(
                  padding: pw.EdgeInsets.all(4),
                  child: pw.Column(
                      crossAxisAlignment: pw.CrossAxisAlignment.start,
                      children: [
                        pw.Text(
                          'Order Date',
                          style: pwTableHeadingTextStyle(),
                        ),
                        pw.Text(orderDate),
                      ]),
                ),
                pw.Padding(
                  padding: pw.EdgeInsets.all(4),
                  child: pw.Column(
                      crossAxisAlignment: pw.CrossAxisAlignment.start,
                      children: [
                        pw.Text(
                          'Required By',
                          style: pwTableHeadingTextStyle(),
                        ),
                        pw.Text(requiredByDate),
                      ]),
                ),
              ],
            ),
          ]),
          pw.Table(children: [
            pw.TableRow(// This is the second row for the table
                children: [
              pw.Padding(
                padding: pw.EdgeInsets.all(4),
                child: pw.Column(
                  crossAxisAlignment: pw.CrossAxisAlignment.start,
                  children: [
                    pw.Text(
                      'Ship To',
                      style: pwTableHeadingTextStyle(),
                    ),
                    pw.Text(ourShipmentAddress),
                  ],
                ),
              ),
            ]),
            //
            // Now the third row of the table
          ]),
          pw.Table(children: [
            pw.TableRow(// This is the third row for the table
                children: [
              pw.Padding(
                padding: pw.EdgeInsets.all(4),
                child: pw.Column(
                  crossAxisAlignment: pw.CrossAxisAlignment.start,
                  children: [
                    pw.Text(
                      'Notes',
                      style: pwTableHeadingTextStyle(),
                    ),
                    pw.Text(notes),
                  ],
                ),
              ),
            ]),
          ]),

          //Gap
          pw.Divider(color: PdfColors.white, thickness: 10),

          //Section Header: Stocked Items
          pw.Header(text: 'Stocked Items'),

          // Stocked Items Table
          // Next Table
          pw.Table(border: pw.TableBorder(), children: [
            pw.TableRow(
              children: [
                paddedHeadingTextCell('Product'),
                paddedHeadingTextCell('Unit'),
                paddedHeadingTextCell('Qty'),
                paddedHeadingTextCell('Price'),
                paddedHeadingTextCell('Discount'),
                paddedHeadingTextCell('Tax'),
                paddedHeadingTextCell('Total AUD'),
              ],
            ),
            // Now the next table row
            buildRowsForInventoryItems(),
          ]),

          //Gap
          pw.Divider(color: PdfColors.white, thickness: 10),

          //Section Header: Stocked Items
          pw.Header(text: 'Custom Build Items and Additional Costs'),
          //

          // Stocked Items Table
          // Next Table
          pw.Table(border: pw.TableBorder(), children: [
            pw.TableRow(
              children: [
                paddedHeadingTextCell('Description'),
                paddedHeadingTextCell('Unit'),
                paddedHeadingTextCell('Qty'),
                paddedHeadingTextCell('Price'),
                paddedHeadingTextCell('Discount'),
                paddedHeadingTextCell('Tax'),
                paddedHeadingTextCell('Total AUD'),
              ],
            ),
            // Now the next table row
            buildRowsForNonInventoryItems(),
          ]),
          //

          // Gap
          pw.Divider(color: PdfColors.white, thickness: 10),

          // Totals heading
          pw.Header(text: 'Totals'),
          // Totals Table
          pw.Table(border: pw.TableBorder(), children: [
            pw.TableRow(children: [
              pw.Padding(
                padding: pw.EdgeInsets.all(4),
                child: pw.Column(children: [
                  pw.Text(' '),
                ]),
              ),
              paddedHeadingTextCell('Stocked Items'),
              paddedHeadingTextCell('Additional Costs'),
              paddedHeadingTextCell('Order Total'),
            ]),
            //
            // Now row 2 (the first row with data)
            pw.TableRow(children: [
              paddedHeadingTextCell('Before Tax'),
              paddedTextCell('_______'),
              paddedTextCell('_______'),
              paddedTextCell('_______'),
            ]),
            //
            // Now row 4 (the third row with data)
            pw.TableRow(children: [
              paddedHeadingTextCell('Tax'),
              paddedTextCell('_______'),
              paddedTextCell('_______'),
              paddedTextCell('_______'),
            ]),
            //
            // Now row 2 (the first row with data)
            pw.TableRow(children: [
              paddedHeadingTextCell('Total'),
              paddedTextCell('_______'),
              paddedTextCell('_______'),
              paddedTextCell('_______'),
            ])
          ]),
//
// Divider
          pw.Divider(color: PdfColors.white, thickness: 10),

          pw.Paragraph(text: kLoremIpsum),
        ];
      }));
}

pw.TableRow buildRowsForInventoryItems() {
  List<pw.Column> itemList = [];

  var items = DatabaseService().returnAllInventoryItemsFromAPurchaseOrder();
  for (var item in items) {
    final description = item.data()['description'].toString();
    final unit = item.data()['unit'].toString();
    final qty = item.data()['qty'].toString();
    itemList.add(
      pw.Column(
        children: [
          pw.Text(description),
        ],
      ),
    );
    itemList.add(
      pw.Column(
        children: [
          pw.Text(unit),
        ],
      ),
    );
    itemList.add(
      pw.Column(
        children: [
          pw.Text(qty),
        ],
      ),
    );
  }
  return pw.TableRow(children: itemList);
}

buildRowsForNonInventoryItems() {
  return pw.TableRow(children: [
    paddedTextCell('_____'),
    paddedTextCell('_____'),
    paddedTextCell('_____'),
    paddedTextCell('_____'),
    paddedTextCell('_____'),
    paddedTextCell('_____'),
    paddedTextCell('_____'),
  ]);
}

pw.TextStyle pwTableHeadingTextStyle() =>
    pw.TextStyle(fontWeight: pw.FontWeight.bold);

pw.Padding paddedTextCell(String textContent) {
  return pw.Padding(
    padding: pw.EdgeInsets.all(4),
    child:
        pw.Column(crossAxisAlignment: pw.CrossAxisAlignment.start, children: [
      pw.Text(textContent, textAlign: pw.TextAlign.left),
    ]),
  );
}

pw.Padding paddedHeadingTextCell(String textContent) {
  return pw.Padding(
    padding: pw.EdgeInsets.all(4),
    child: pw.Column(children: [
      pw.Text(
        textContent,
        style: pwTableHeadingTextStyle(),
      ),
    ]),
  );
}

class _reportingState extends State<reporting> {
  @override
  Widget build(BuildContext context) {
    final pdf = pw.Document();
    createPdfDocument(pdf);

    final bytes = pdf.save();
    final blob = html.Blob([bytes], 'application/pdf');

    return Scaffold(
      appBar: AppBar(
        title: Text('Search for item'),
      ),
      drawer: myGlobalDrawer(),
      bottomNavigationBar: kStandardBottomNavigationBarForNoFAB(),
      floatingActionButton: null,
      body: Column(
        children: [
        
          MaterialButton(
              child: Text('Create and view in browser'),
              color: Colors.yellow,
              onPressed: () async {
                print('i got tapped');

                final url = html.Url.createObjectUrlFromBlob(blob);
                html.window.open(url, "_blank");
                html.Url.revokeObjectUrl(url);
              }),
          RaisedButton(
            child: Text("Create and download"),
            onPressed: () {
              final url = html.Url.createObjectUrlFromBlob(blob);
              final anchor =
                  html.document.createElement('a') as html.AnchorElement
                    ..href = url
                    ..style.display = 'none'
                    ..download = 'some_name.pdf';
              html.document.body.children.add(anchor);
              anchor.click();
              html.document.body.children.remove(anchor);
              html.Url.revokeObjectUrl(url);
            },
          ),
        ],
      ),
    );
  }
}
Gamy answered 27/11, 2020 at 12:14 Comment(0)
A
7

Something like this will work: Each row has n columns regarding how many properties you have; now, reportedItems is a list that holds data coming from Firestore or any other place...

pw.Table(
 children: [
    for (var i = 0; i < reportedItems.length; i++)
                   pw.TableRow(
                       children: [
                         pw.Column(
                             crossAxisAlignment: pw.CrossAxisAlignment
                                 .center,
                             mainAxisAlignment: pw.MainAxisAlignment.center,
                             children: [
                               pw.Text(reportedItems[i][0],
                                   style: pw.TextStyle(fontSize: 6)),
                               pw.Divider(thickness: 1)
                             ]
                         ),
                         pw.Column(
                             crossAxisAlignment: pw.CrossAxisAlignment
                                 .center,
                             mainAxisAlignment: pw.MainAxisAlignment.center,
                             children: [
                               pw.Text(reportedItems[i][1],
                                   style: pw.TextStyle(fontSize: 6)),
                               pw.Divider(thickness: 1)
                             ]
                         ),
                         pw.Column(
                             crossAxisAlignment: pw.CrossAxisAlignment
                                 .center,
                             mainAxisAlignment: pw.MainAxisAlignment.center,
                             children: [
                               pw.Text(reportedItems[i][2],
                                   style: pw.TextStyle(fontSize: 6)),
                               pw.Divider(thickness: 1)
                             ]
                         ),
                         pw.Column(
                             crossAxisAlignment: pw.CrossAxisAlignment
                                 .center,
                             mainAxisAlignment: pw.MainAxisAlignment.center,
                             children: [
                               pw.Text(reportedItems[i][3],
                                   style: pw.TextStyle(fontSize: 6)),
                               pw.Divider(thickness: 1)
                             ]
                         )
                       ]
                   )
           ]
        )
Animadvert answered 20/3, 2021 at 17:50 Comment(0)
H
0

There is an easy way to do so:

  1. Put your data in a list

  2. Generate the children of TableRow from that list.

        List myList = ['first', 'second'];
        .
        .
        .
    
        Table(
          children: List.generate(
            myList.length,
            (index) {
              final listData = myList[index];
              return TableRow(
                children: [
                  Text(listData),
                ],
              );
            },
          ),
        )
    

Also you can achieve that with List<YourClassModel> assume that you have in your class model variables as String name; and String address; so you have to only use that as listData.name and listData.address.

Homburg answered 7/12, 2022 at 16:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.