flutter_pdf_invoice_widgets 1.0.1
flutter_pdf_invoice_widgets: ^1.0.1 copied to clipboard
Customizable, server-compatible PDF invoices, receipts, quotes, credit notes, and delivery documents for Dart and Flutter.
flutter_pdf_invoice_widgets #
A comprehensive Flutter package providing 10 highly customizable PDF invoice and document templates.
#Flutter #Dart #PDF #Invoice #InvoiceGenerator #PDFGeneration #DocumentTemplates #OpenSource
The package is UI-independent, performs no network requests, and returns PDF bytes that can be saved, returned from a server, previewed, printed, or shared.
Features #
- Ten built-in templates: Classic, Modern, Professional, Minimalist, Creative, Receipt, Proforma, Quote/Estimate, Delivery Note, and Credit Note.
- Typed, schema-versioned
InvoiceDocumentmodel with JSON round trips. - Centralized rounded calculations for line tax/discount, tax-inclusive prices, document discounts, fees, shipping, withholding, payments, and balance due.
- Typed validation with stable issue codes and useful field paths.
- Custom columns, stable label keys, per-column alignment, and independent table border controls.
- Locale-aware currency, number, percentage, and date formatting.
- LTR/RTL page direction, Arabic and English label bundles, custom Unicode fonts, and font fallback lists.
- Logos, generated or image-based QR codes, watermarks, signatures, metadata, custom sections, progress reporting, and cooperative cancellation.
- Multi-page tables with repeated headers and stress coverage for 150-line documents across all templates.
- Pure Dart runtime compatible with Flutter, command-line, server, desktop, and
web environments supported by the underlying
pdfpackage.
Requirements #
- Dart
>=3.12.0 <4.0.0. pdf^3.13.0andintl^0.20.0.
The Dart minimum follows the current pdf dependency's minimum. Flutter
applications can use the package normally; Flutter is not forced into server or
command-line consumers.
Installation #
dependencies:
flutter_pdf_invoice_widgets: ^1.0.1
Then run dart pub get or flutter pub get.
Quick start #
import 'dart:io';
import 'package:flutter_pdf_invoice_widgets/flutter_pdf_invoice_widgets.dart';
Future<void> main() async {
final document = InvoiceDocument(
type: InvoiceDocumentType.modernInvoice,
company: CompanyInfo(
name: 'Acme Studio',
city: 'Riyadh',
country: 'Saudi Arabia',
taxId: 'VAT-123456789',
),
customer: CustomerInfo(name: 'Example Customer'),
info: InvoiceInfo(
invoiceNumber: 'INV-2026-001',
invoiceDate: DateTime(2026, 8, 9),
dueDate: DateTime(2026, 9, 8),
currency: 'SAR',
currencySymbol: 'SAR ',
statusValue: InvoiceStatus.unpaid,
terms: 'Payment is due within 30 days.',
),
items: [
InvoiceItem(
description: 'Flutter development',
quantity: 24,
unit: 'hours',
unitPrice: 250,
taxRate: 15, // 15 means 15%.
),
],
payment: PaymentInfo(
paymentMethods: ['Bank transfer', 'Card'],
qrData: 'https://example.com/pay/INV-2026-001',
),
);
final template = InvoiceTemplateRegistry().create(document);
final bytes = await template.generate();
await File('invoice.pdf').writeAsBytes(bytes);
}
See the complete runnable example.
Templates #
| Document type | Direct class | Default character |
|---|---|---|
classicInvoice |
ClassicInvoice |
Traditional bordered invoice |
modernInvoice |
ModernInvoice |
Contemporary color header |
professionalInvoice |
ProfessionalInvoice |
Corporate layout |
minimalistInvoice |
MinimalistInvoice |
Typography-first layout |
creativeInvoice |
CreativeInvoice |
Bold colors and shapes |
receipt |
ReceiptTemplate |
Dynamically sized receipt roll |
proformaInvoice |
ProformaInvoice |
Proforma watermark and validity |
quoteEstimate |
QuoteEstimate |
Customer acceptance section |
deliveryNote |
DeliveryNote |
Shipping data and received markers |
creditNote |
CreditNote |
Credit reference and message |
Image Samples #
Classic invoice #

Modern invoice #

The registry is recommended for data-driven applications. Direct construction remains supported:
final bytes = await ClassicInvoice(
company: CompanyInfo(name: 'Seller'),
customer: CustomerInfo(name: 'Buyer'),
info: InvoiceInfo(
invoiceNumber: 'INV-1',
invoiceDate: DateTime.now(),
),
items: [
InvoiceItem(description: 'Service', quantity: 1, unitPrice: 100),
],
).generate();
Financial calculations #
InvoiceCalculator is the single authoritative calculation path used by the
document model and every built-in template. With the default policy, calculated
line values and aggregates are rounded to two decimal places.
final document = InvoiceDocument(
company: company,
customer: customer,
info: info,
items: items,
taxInclusive: false,
monetaryPolicy: const MonetaryPolicy(decimalPlaces: 2),
adjustments: [
InvoiceAdjustment(
label: 'Customer discount',
type: InvoiceAdjustmentType.discount,
calculation: InvoiceAdjustmentCalculation.percentage,
value: 10,
),
InvoiceAdjustment(
label: 'Shipping',
type: InvoiceAdjustmentType.shipping,
value: 25,
),
InvoiceAdjustment(
label: 'Processing fee',
type: InvoiceAdjustmentType.fee,
value: 5,
),
],
amountPaid: 50,
);
print(document.totals.grandTotal);
print(document.totals.balanceDue);
Calculation order:
- Quantity × unit price is rounded for each line.
- The line discount is rounded and subtracted.
- Exclusive tax is added, or inclusive tax is extracted.
- Rounded lines are aggregated.
- Document adjustments are calculated from the post-line-tax total.
- Discounts and withholding are subtracted; shipping and fees are added.
- Amount paid is subtracted to produce balance due.
When both a rate and explicit amount are supplied for the same line tax or discount, validation reports an ambiguity. Choose one representation.
Credit Note signs #
The package never silently changes signs. Use positive values when the Credit Note title communicates the business meaning, or negative lines when your accounting convention requires signed exports. Lines and totals keep the values you supplied.
Validation #
The registry validates documents automatically. Validation can also be performed before rendering:
final issues = document.validate();
for (final issue in issues) {
print('${issue.code}: ${issue.field} — ${issue.message}');
}
document.validateOrThrow();
Validation covers required text, date order, finite numbers, percentage ranges, ambiguous tax/discount inputs, and adjustment values. Empty item lists are valid and render a table header with zero totals.
Styling #
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
final style = InvoiceStyle(
primaryColor: PdfColors.indigo,
accentColor: PdfColors.amber,
backgroundColor: PdfColors.white,
pageFormat: PdfPageFormat.a4,
pageMargin: const pw.EdgeInsets.all(40),
headerStyle: const HeaderStyle(
showLogo: true,
showCompanyName: true,
showCompanyDetails: true,
padding: pw.EdgeInsets.all(12),
),
footerStyle: const FooterStyle(
showPageNumbers: true,
showDate: true,
customText: 'Confidential',
),
tableStyle: TableStyle(
showBorder: true,
showHeaderBorder: true,
showRowBorders: false,
),
watermarkText: 'DRAFT',
signatureLabel: 'Authorized signature',
);
Presets are available through InvoiceStyle.classic(), .modern(), .dark(),
and .colorful(). Page backgrounds are painted by every template, including the
dark preset.
HeaderStyle controls the shared header used by Classic, Proforma, Quote,
Delivery Note, and Credit Note. Modern, Professional, Minimalist, Creative, and
Receipt have document-specific headers; their logo/company layout is controlled
by the company data and the template design. FooterStyle applies to all
multi-page templates; Receipt has a document-specific ending instead.
Custom columns and labels #
Column IDs are stable label and data keys. Unknown IDs read from
InvoiceItem.customColumns.
final style = InvoiceStyle(
labels: {
'description': 'Service',
'quantity': 'Hours',
'project': 'Project',
},
tableStyle: TableStyle(
columns: [
InvoiceColumn.index(),
InvoiceColumn.description(),
InvoiceColumn(id: 'project', header: 'Project', flex: 2),
InvoiceColumn.quantity(),
InvoiceColumn.unitPrice(),
InvoiceColumn.tax(),
InvoiceColumn.total(),
],
),
);
final item = InvoiceItem(
description: 'Implementation',
quantity: 10,
unitPrice: 100,
customColumns: {'project': 'Mobile app'},
);
Use InvoiceColumn.formatter for calculated custom text. Per-column alignment is
honored for headers and cells.
Localization, Unicode, and RTL #
final rtlStyle = InvoiceStyle(
localization: InvoiceLocalization.arabic(),
primaryFont: arabicRegularFont,
boldFont: arabicBoldFont,
fontFallback: [fallbackFont],
labels: {'invoice': 'فاتورة ضريبية'},
);
InvoiceLocalization controls locale-aware dates/currency, text direction, and
built-in labels. Custom labels override the bundle.
Important: PDF standard fonts do not cover Arabic and many Unicode scripts. This package does not bundle or download fonts. Load appropriately licensed TTF fonts from bytes and provide them explicitly:
final regular = PdfFontLoader.fromBytes(regularTtfBytes);
final bold = PdfFontLoader.fromBytes(boldTtfBytes);
final style = InvoiceStyle(
primaryFont: regular,
boldFont: bold,
fontFallback: [regular],
localization: InvoiceLocalization.arabic(),
);
In Flutter, obtain the bytes with rootBundle.load; on Dart/server, use
File.readAsBytes, an asset system, or your own storage layer. Fonts are never
fetched over the network implicitly.
JSON persistence #
import 'dart:convert';
final encoded = jsonEncode(document.toJson());
final restored = InvoiceDocument.fromJson(
(jsonDecode(encoded) as Map).cast<String, Object?>(),
);
The schema includes a version and rejects unknown versions. Dates use UTC ISO 8601 strings; images use base64; enums use stable names. Runtime-only values such as PDF fonts, callbacks, and style objects are intentionally not serialized.
Metadata and custom sections #
import 'package:pdf/widgets.dart' as pw;
final template = InvoiceTemplateRegistry().create(
document,
metadata: const InvoicePdfMetadata(
title: 'Invoice INV-2026-001',
subject: 'Consulting services',
keywords: 'invoice,consulting',
),
beforeItemsBuilder: (context) => [
pw.Text('Project: Phoenix', style: context.style.boldBodyTextStyle),
pw.SizedBox(height: 12),
],
afterItemsBuilder: (context) => [
pw.Text('Internal reference: PO-42'),
],
);
For a completely custom design, extend BaseTemplate or register a custom
factory with InvoiceTemplateRegistry.register.
Progress and cancellation #
var cancelled = false;
final bytes = await template.generate(
onProgress: (value) => print('${(value * 100).round()}%'),
isCancelled: () => cancelled,
);
Cancellation is checked before validation, before layout, and before saving. PDF layout itself is synchronous in the underlying library; use an isolate for large documents when UI responsiveness matters.
Flutter preview, print, and share #
Add the printing package to the consuming Flutter application, not to this core
package:
import 'package:printing/printing.dart';
await Printing.layoutPdf(onLayout: (_) async => pdfBytes);
await Printing.sharePdf(bytes: pdfBytes, filename: 'invoice.pdf');
This keeps Flutter-only dependencies out of server applications.
Performance and visual review #
- Run
dart run benchmark/generate_large_invoice.dartfor a reproducible 1,000-line generation benchmark. - Run
dart run tool/generate_catalog.dartto generate the same reference data through every built-in template underbuild/catalog. - Follow RELEASE_CHECKLIST.md to render and visually inspect the catalog before publishing.
Resize high-resolution images before passing them to the package; embedded image
bytes affect memory use and PDF size. maxPages defaults to 100 and can be
increased explicitly for unusually large documents.
Scope and compliance #
Version 1.0.0 does not claim PDF encryption, cryptographic signatures, PDF/A, tagged-PDF accessibility, or jurisdiction-specific tax/e-invoicing compliance. QR payloads are rendered exactly as supplied. Consult qualified specialists and official specifications before using generated documents for regulated flows.
Quality checks #
The repository runs formatting, static analysis, tests, examples, stress generation, and a publish dry-run. CI covers the minimum and latest supported Dart SDKs. See MIGRATION.md, RELEASE_CHECKLIST.md, and SECURITY.md.
License #
MIT License. See LICENSE.