simple_pdf_generator

A lightweight Flutter package for generating PDFs from structured data with minimal boilerplate.

Overview

Building PDFs in Flutter often means a lot of repetitive layout code: tables, alignment, row formatting, headers, and footers. This package focuses on that workflow so you can pass structured data (for example maps or API models) and get a usable PDF with a small, readable API.

What it helps with

  • Less repetitive PDF layout code
  • Tables built from lists and optional column mapping
  • Consistent header and footer blocks
  • Faster reporting, billing, and export features

Features

  • Simple SimplePdf.generate API
  • Multiple tables in a single PDF (tables: List<PdfTable>), or mixed layout via sections: List<PdfSection> (PdfTable + PdfTableRow for side‑by‑side tables)
  • Tables from structured data with optional mapper for dynamic or nested sources
  • Per-table summary sections (summaryHeaders + summaryData)
  • Per-table styling:
    • Table header styling (PdfTableHeaderStyle)
    • Table cell styling (PdfTableCellStyle)
    • Summary styling (PdfSummaryStyle)
  • Multilingual table text support (Unicode) with the package theme font
  • Table body cells can mix text and images (PdfTableCell, or shorthand String / Uint8List in row maps)
  • Page orientation support: portrait (default) and landscape
  • Configurable header (title, subtitle, extra) and optional footer
  • Small surface area and a thin dependency on pdf

Installation

Add simple_pdf_generator to your pubspec.yaml.

From pub.dev:

dependencies:
  simple_pdf_generator: ^0.2.3

Run flutter pub get.

Usage

Import the library:

import 'package:simple_pdf_generator/simple_pdf_generator.dart';

Basic example

When your rows are already maps whose keys match the column headers:

final pdf = await SimplePdf.generate(
  header: PdfHeader(
    title: 'My Organization',
    subtitle: 'Summary Report',
    extra: '01/01/2026 – 10/01/2026',
  ),
  tables: [
    PdfTable(
      headers: ['Name', 'Age'],
      data: [
        {'Name': 'John', 'Age': 28},
        {'Name': 'Alice', 'Age': 24},
      ],
    ),
  ],
  footer: PdfFooter(text: 'Generated by simple_pdf_generator'),
);

Multilingual text + image cells in tables

Tables are now designed for mixed content:

  • String / PdfTableCell.text(...) for normal Unicode text (multilingual)
  • PdfTableCell.image(...) or raw Uint8List for image cells in any column

Recommended canonical form: use PdfTableCell so each column’s intent is clear.

  • Text (Unicode / multilingual): PdfTableCell.text('...') or a plain String. Text uses the same document theme as today (bundled Noto by default), so multilingual content renders as normal text in tables.
  • Images: PdfTableCell.image(uint8List, maxWidth: …, maxHeight: …, fit: …). Omitting maxWidth / maxHeight applies defaults of 40×40 PDF points so rows stay compact. fit is a Flutter BoxFit and is mapped to the pdf layout engine (default contain).
  • Shorthand: a raw Uint8List in the row map is treated as image bytes with those same default bounds.

Example with mixed columns (Label, Photo, Amount):

final logoPng = await File('logo.png').readAsBytes();

final pdf = await SimplePdf.generate(
  header: PdfHeader(title: 'Report'),
  tables: [
    PdfTable(
      headers: ['Label', 'Photo', 'Amount'],
      data: [
        {
          'Label': 'Line item',
          'Photo': PdfTableCell.image(logoPng, maxWidth: 32, maxHeight: 32),
          'Amount': r'$10.00',
        },
        {
          'Label': 'Another row',
          'Photo': logoPng, // same as image cell with default 40×40 max size
          'Amount': '5.00',
        },
      ],
    ),
  ],
);

String-only tables are unchanged and still use the existing fast text-table path.

In short: existing string tables keep working without changes, and now the same table can also include image cells when needed.

Using a mapper

Use mapper when your source data does not match header names (for example API or database records):

final pdf = await SimplePdf.generate(
  header: PdfHeader(title: 'Report'),
  tables: [
    PdfTable(
      headers: ['Date', 'Amount'],
      data: apiResponse,
      mapper: (item) => {
        'Date': item['date'].toString(),
        'Amount': item['amount'].toString(),
      },
    ),
  ],
);

Page orientation (portrait / landscape)

SimplePdf.generate supports both portrait and landscape pages:

  • Portrait (default): no extra flag needed
  • Landscape: set pageLandscape: true
  • Custom page format: pass pageFormat; when provided, it overrides pageLandscape
final pdf = await SimplePdf.generate(
  header: PdfHeader(title: 'Landscape report'),
  tables: [
    PdfTable(
      headers: ['Name', 'Address', 'Present', 'Total Days', 'Attd.'],
      data: rows,
    ),
  ],
  pageLandscape: true, // A4 landscape
);

Side-by-side tables (PdfTableRow)

Use sections when you need a full-width table and then several tables on one horizontal row (equal width per table, with gaps). Each PdfTable still owns its own headerStyle, cellStyle, and summary.

  • Pass sections: [ ... ] where each element is a PdfTable or a PdfTableRow.
  • If sections is non-null and not empty, it is used and tables / table are ignored.
  • PdfTableRow options: horizontalGap, verticalPaddingBefore, verticalPaddingAfter (defaults: 8 / 12 / 12 pt). Normal vertical spacing between sections still applies (kSimplePdfTableSpacing).
  • Validation: before layout, each table’s estimated minimum width is compared to its share of the page body width (pageFormat.availableWidth). If any table does not fit, generation throws StateError with:
    Tables exceed available width in PdfTableRow. Reduce columns or number of tables.
  • Tables inside a row cannot use startOnNewPage: true.
final pdf = await SimplePdf.generate(
  header: PdfHeader(title: 'Dashboard'),
  sections: [
    PdfTable(
      headers: ['Metric', 'Value'],
      data: const [
        {'Metric': 'Total', 'Value': '100'},
      ],
    ),
    PdfTableRow(
      tables: [
        PdfTable(headers: ['A'], data: const [{'A': '1'}]),
        PdfTable(headers: ['B'], data: const [{'B': '2'}]),
      ],
    ),
    PdfTable(
      headers: ['Note'],
      data: const [{'Note': 'Full width again'}],
    ),
  ],
);

Sample PDF mixing full-width tables with 2-up and 3-up PdfTableRow sections:

Sample PDF: PdfTableRow side-by-side tables demo

Multiple tables + per-table summary

final pdf = await SimplePdf.generate(
  header: PdfHeader(title: 'Daily Report'),
  tables: [
    PdfTable(
      headers: ['Date', 'Qty'],
      data: dailyRows,
    ),
    PdfTable(
      headers: ['Item', 'Amount'],
      data: billingRows,
      summaryHeaders: const ['Label', 'Value'],
      summaryData: const [
        ['Subtotal', '4500'],
        ['Tax', '225'],
        ['Total', '4725'],
      ],
    ),
  ],
);

Multi-table report example (with per-table summaries)

final pdf = await SimplePdf.generate(
  header: PdfHeader(
    title: 'Springfield School',
    subtitle: 'Students Report',
    extra: 'Term 1 - 2026',
  ),
  tables: [
    PdfTable(
      headers: ['Student', 'Math', 'Science', 'English', 'Total'],
      data: marksRows,
      summaryHeaders: const ['Metric', 'Value'],
      summaryData: const [
        ['Students', '2'],
        ['Avg Math', '86.5'],
        ['Avg Science', '80.0'],
        ['Avg English', '83.5'],
        ['Avg Total / Student', '250.0'],
      ],
    ),
    PdfTable(
      headers: ['Student', 'Present', 'Total Days', 'Attendance %'],
      data: attendanceRows,
      summaryHeaders: const ['Metric', 'Value'],
      summaryData: const [
        ['Students', '2'],
        ['Total Present Days', '82'],
        ['Total School Days', '90'],
        ['Overall Attendance %', '91.1%'],
      ],
    ),
  ],
  footer: PdfFooter(text: 'Generated by simple_pdf_generator'),
);

Styling (header, cells, and summary)

final pdf = await SimplePdf.generate(
  header: PdfHeader(title: 'Styled Report'),
  tables: [
    PdfTable(
      headers: ['A', 'B'],
      data: rows,
      headerStyle: const PdfTableHeaderStyle(
        backgroundColor: SimplePdfColor.blue100,
        textColor: SimplePdfColor.blue900,
      ),
      cellStyle: const PdfTableCellStyle(
        fontSize: 12,
        textColor: SimplePdfColor.grey900,
        fontFamily: PdfFontFamily.poppins,
      ),
      summaryHeaders: const ['Label', 'Value'],
      summaryData: const [
        ['Total', '5000'],
        ['Tax', '250'],
      ],
      summaryStyle: const PdfSummaryStyle(
        headerStyle: PdfSummaryHeaderStyle(
          backgroundColor: SimplePdfColor.grey100,
          textColor: SimplePdfColor.grey700,
        ),
        cellStyle: PdfSummaryCellStyle(
          labelColor: SimplePdfColor.grey800,
          valueColor: SimplePdfColor.blue900,
        ),
        backgroundColor: SimplePdfColor.grey50,
        fontFamily: PdfFontFamily.lato,
      ),
    ),
  ],
);

generate returns a Document from the pdf package. Persist it with save():

import 'dart:io';

// ...

final bytes = await pdf.save();
final file = File('${directory.path}/report.pdf');
await file.writeAsBytes(bytes);

To open the file on device, you can use packages such as path_provider and open_file_safe. See the example/ app in this repository for a full flow.

Example output

The following sample was generated with the example app: header, tabular data (with a mapper), and footer.

Sample PDF: summary report with table

Multi-table example with per-table summaries:

Sample PDF: multi table with summaries

Multilingual table example (Unicode text in table cells):

Sample PDF: multilingual table text

PdfTableRow demo (full-width tables plus side-by-side tables in one row):

Sample PDF: PdfTableRow demo

More features are on the way—see Roadmap for planned work.

API summary

Type Role
SimplePdf generate(...) builds the PDF document
PdfHeader title (required), optional subtitle, extra
PdfSection Base type: PdfTable or PdfTableRow for sections
PdfTable headers, data, optional mapper, optional per-table summary + styling
PdfTableRow tables: several PdfTables in one horizontal row (equal width)
PdfTableCell text or image cell for mixed text/PNG (or String / Uint8List in maps)
PdfFooter Optional text

Roadmap

  • Multiple templates (invoice, receipt, and similar)
  • Sorting and richer formatting
  • Multi-language support
  • More layout options (alignment, spacing, section layouts)

Contributing

Issues and pull requests are welcome.

License

MIT License. See LICENSE.

Libraries

simple_pdf_generator
Build PDFs from structured data with a small API: SimplePdf.generate, PdfHeader, PdfSection / PdfTable / PdfTableRow, PdfTableCell for text/image cells, and PdfFooter.