title: Create dynamic table rows in PDF document using the pdf package
body: <p><strong>Background</strong>
  My Flutter app uses the <code>pdf/pdf.dart</code> 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.</p>
  <p><strong>Current State</strong>
  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.</p>
  <p><strong>Question</strong>
  How can I dynamically and programatically create one row in the table for each record in a nominated Firestore collection?</p>
  <p><strong>Code Snippet: The Table</strong></p>
  <pre><code>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></pre>
  <p><strong>Code Snippit: The Non-functional Row builder</strong></p>
  <pre><code>pw.TableRow buildRowsForInventoryItems() {
    List&lt;pw.Column&gt; 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);
  }
  </code></pre>
  <p><strong>Complete Code</strong></p>
  <pre><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() =&gt; _reportingState();
  }
  
  final pdf = pw.Document();
  String orderNumber = '&lt;&lt;Order_Number&gt;&gt;';
  String disclaimerText = '&lt;&lt;Disclaimer_Text&gt;&gt;';
  String supplierEmailAddress = '&lt;&lt;Supplier_Name&gt;&gt;';
  String supplierName = '&lt;&lt;Supplier_Email_Address&gt;&gt;';
  String orderDate = '&lt;&lt;Order_Date&gt;&gt;';
  String requiredByDate = '&lt;&lt;Required_By_Date&gt;&gt;';
  String ourShipmentAddress = '&lt;&lt;Our_Shipment_Address&gt;&gt;';
  String notes = '&lt;&lt;Notes&gt;&gt;';
  
  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 &lt;pw.Widget&gt;[
  // 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&lt;pw.Column&gt; 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() =&gt;
      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&lt;reporting&gt; {
    @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, &quot;_blank&quot;);
                  html.Url.revokeObjectUrl(url);
                }),
            RaisedButton(
              child: Text(&quot;Create and download&quot;),
              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);
              },
            ),
          ],
        ),
      );
    }
  }
  </code></pre>