invoice_kit 0.1.0
invoice_kit: ^0.1.0 copied to clipboard
Generate customizable invoice, bill, and receipt PDFs in Flutter with multiple templates, JSON-driven layouts, GST support, QR codes, and cross-platform export.
invoice_kit #
Production-ready Flutter package for generating customizable invoices, bills, and receipts as PDFs.
Features #
- ๐จ 5 Built-in Templates: Classic (B2B with GST), Modern (minimalist), Compact (events/retail), Thermal (80mm POS), JSON-driven (fully dynamic)
- ๐ Dual Data API: Strongly-typed
Invoicemodels OR raw JSONInvoiceDatafor server-driven flows - ๐ฐ GST-Ready: CGST/SGST/IGST breakdown, multi-currency (default โน INR), multi-locale formatting
- ๐ค Full Unicode Support: Hindi, Tamil, Arabic, Chinese, etc. via Google Fonts (Roboto, Noto Sans)
- ๐ฑ QR Codes: UPI payment links and custom QR code generation
- ๐จ๏ธ Cross-Platform Actions: Generate, download, share, print, save to device
- ๐ True Cross-Platform: Android, iOS, Web, Windows, macOS, Linux
- ๐งฉ Custom Templates: Implement
InvoiceTemplateinterface for custom designs - ๐ JSON Templates: White-label and server-driven invoices via JSON specs
- ๐ Type-Safe: Sound null safety with comprehensive error handling
- ๐งช Well-Tested: Unit tests with 100% pass rate
Installation #
Add to pubspec.yaml:
dependencies:
invoice_kit: ^0.1.0
Or run:
flutter pub add invoice_kit
Quick Start (30 seconds) #
import 'package:invoice_kit/invoice_kit.dart';
// 1. Create an invoice
final invoice = Invoice(
number: 'INV-001',
date: DateTime.now(),
seller: Party(name: 'My Business'),
buyer: Party(name: 'John Doe'),
items: [
LineItem(
description: 'Professional Service',
quantity: 10,
unitPrice: 1000,
taxPercent: 18,
),
],
);
// 2. Generate PDF
final bytes = await InvoiceGenerator.generate(
invoice: invoice,
template: ClassicTemplate(),
);
// 3. Download / Share / Print
await InvoiceGenerator.download(
invoice: invoice,
filename: 'INV-001.pdf',
);
Platform Behavior #
| Feature | Web | Android | iOS | Desktop |
|---|---|---|---|---|
generate() |
โ Returns Uint8List | โ | โ | โ |
preview() |
โ Inline PdfPreview | โ | โ | โ |
download() |
โ Browser download | โ Share sheet | โ Share sheet | โ Save dialog |
saveToDevice() |
โ ๏ธ Falls back to download (returns null) | โ App docs dir | โ App docs dir | โ App docs dir |
share() |
โ Web Share API or fallback | โ Native share | โ Native share | โ Native share |
printDocument() |
โ Browser print | โ Android print service | โ AirPrint | โ System print |
Architecture: Conditional Imports #
The package uses Dart's conditional imports to provide platform-specific implementations without bloating the bundle:
- Web: Uses
dart:htmlfor browser downloads + Printing API for print/share - Mobile/Desktop: Uses
dart:io+path_providerfor file I/O - Compile-time selection: Your web build doesn't include
dart:io, and Android builds don't includedart:html
See lib/src/actions/pdf_actions.dart and related implementation files for details.
Why download() opens a share sheet on Android #
Android's scoped storage (API 29+) restricts direct file system access for security. The recommended UX is to let the user pick the destination via the system share sheet (which download() does). If you need a guaranteed file path, use saveToDevice() which returns the full path to the saved file.
Web font loading & โน rendering #
On web, Google Fonts (Roboto, Noto Sans) are fetched at runtime via the Printing API. The first PDF generation may take ~200ms longer; subsequent calls are cached. The โน symbol and other Unicode characters render correctly out of the box (Noto Sans provides full script support).
Strongly-Typed API #
Perfect for app code with compile-time type safety:
final invoice = Invoice(
number: 'INV-001',
date: DateTime.now(),
seller: Party(
name: 'Acme Corp',
address: '123 Business St',
city: 'New Delhi',
state: 'DL',
postalCode: '110001',
country: 'India',
gstin: '23ABCDE1234F2G5',
bankName: 'State Bank of India',
bankAccount: '00123456789012',
ifscCode: 'SBIN0001234',
upiId: 'acme@okhdfcbank',
),
buyer: Party(
name: 'Customer Name',
email: 'customer@example.com',
phone: '+91-9876543210',
address: '456 Customer Ave',
city: 'Mumbai',
state: 'MH',
),
items: [
LineItem(
description: 'Web Development Services',
quantity: 40,
unitPrice: 2500,
cgstPercent: 9,
sgstPercent: 9,
),
LineItem(
description: 'UI/UX Design',
quantity: 20,
unitPrice: 1500,
discountPercent: 10,
cgstPercent: 9,
sgstPercent: 9,
),
],
payment: Payment(
method: 'Bank Transfer',
bankName: 'HDFC Bank',
dueDate: DateTime.now().add(Duration(days: 30)),
),
status: 'Issued',
notes: 'Thank you for your business!',
terms: 'Payment due within 30 days of invoice date.',
);
// Auto-calculated totals
print(invoice.totals.grandTotal); // Automatically computed
JSON API #
Perfect for server-driven flows and white-label scenarios:
final data = InvoiceData({
'invoice': {
'number': 'INV-001',
'date': DateTime.now().toIso8601String(),
'status': 'Issued',
},
'seller': {
'name': 'Acme Inc',
'gstin': '23ABCDE1234F2G5',
'address': '123 Business St',
'city': 'New Delhi',
},
'buyer': {
'name': 'Customer',
'email': 'customer@example.com',
},
'items': [
{
'description': 'Service',
'quantity': 1,
'unitPrice': 5000,
'taxPercent': 18,
},
],
});
final bytes = await InvoiceGenerator.generate(data: data);
Seamless conversion between both APIs:
// Invoice โ InvoiceData
final data = invoice.toInvoiceData();
// InvoiceData โ Invoice
final invoice = Invoice.fromInvoiceData(data);
Built-in Templates #
1. ClassicTemplate #
Detailed B2B invoice with GST breakdown, bank details, and signature block. Perfect for invoicing and compliance.
final pdf = await ClassicTemplate().build(data);
2. ModernTemplate #
Clean minimalist design with accent color sidebar and generous whitespace. Ideal for agencies, SaaS, and freelancers.
final pdf = await ModernTemplate().build(data);
3. CompactTemplate #
Compact invoice for events, retail, and service businesses. Matches common POS receipt layouts.
final pdf = await CompactTemplate().build(data);
4. ThermalTemplate #
80mm thermal printer receipt for POS systems, restaurants, and retail counters.
final pdf = await ThermalTemplate().build(data);
5. JsonTemplate #
Fully dynamic JSON-driven templates for white-label and server-driven scenarios.
final layoutJson = {
'id': 'custom_v1',
'pageFormat': 'A4',
'body': [
{'type': 'text', 'content': 'Invoice {{invoice.number}}'},
{'type': 'items_table'},
{'type': 'totals_box'},
],
};
final pdf = await JsonTemplate(layoutJson).build(data);
Theming #
Customize colors, fonts, images, and more:
final config = TemplateConfig(
primaryColor: ColorUtils.hexToColor('#4A90B8'),
logoAssetPath: 'assets/logo.png',
logoBytes: await loadImageBytes(),
signatureImagePath: 'assets/signature.png',
baseFont: await FontLoader.robotoRegular(),
boldFont: await FontLoader.robotoBold(),
currencySymbol: 'โน',
currencyCode: 'INR',
locale: 'en_IN',
dateFormat: 'dd-MM-yyyy',
footerText: 'Thank you for your business!',
showSignature: true,
showQrCode: true,
qrData: 'upi://pay?pa=acme@okhdfcbank&pn=Acme%20Inc&am=5000',
);
final bytes = await InvoiceGenerator.generate(
invoice: invoice,
template: ClassicTemplate(),
config: config,
);
Custom Templates #
Implement the InvoiceTemplate interface:
class MyCustomTemplate extends InvoiceTemplate {
@override
String get id => 'my_custom';
@override
String get name => 'My Custom Template';
@override
String get description => 'My custom invoice design';
@override
PdfPageFormat get pageFormat => PdfPageFormat.a4;
@override
Future<pw.Document> build(
InvoiceData data, {
TemplateConfig? config,
}) async {
final fonts = await FontLoader.autoLoad(data);
final doc = pw.Document();
doc.addPage(
pw.Page(
build: (context) => pw.Center(
child: pw.Text(
'Invoice: ${data.get('invoice.number')}',
style: pw.TextStyle(font: fonts.bold, fontSize: 24),
),
),
),
);
return doc;
}
}
// Use your custom template
final bytes = await InvoiceGenerator.generate(
invoice: invoice,
template: MyCustomTemplate(),
);
Placeholder Templating #
Use {{dot.notation.paths}} with optional formatters:
final resolver = PlaceholderResolver(data);
// Simple paths
resolver.resolve('Invoice {{invoice.number}}'); // "Invoice INV-001"
// Array indexing
resolver.resolve('First item: {{items.0.description}}');
// Formatters (pipe syntax)
resolver.resolve('Amount: {{totals.grandTotal | currency}}');
resolver.resolve('Date: {{invoice.date | date:dd-MM-yyyy}}');
resolver.resolve('Name: {{buyer.name | upper}}');
Supported formatters: currency, number, percent, date:pattern, upper, lower, title, trim, length.
Cross-Platform Actions #
All actions work seamlessly across platforms:
// Generate PDF bytes
final bytes = await InvoiceGenerator.generate(invoice: invoice);
// Save to device (mobile/desktop) โ returns File
final file = await InvoiceGenerator.saveToDevice(
invoice: invoice,
filename: 'INV-001.pdf',
);
// Download (web: browser dialog, mobile: share sheet, desktop: save dialog)
await InvoiceGenerator.download(
invoice: invoice,
filename: 'INV-001.pdf',
);
// Native share sheet
await InvoiceGenerator.share(
invoice: invoice,
filename: 'INV-001.pdf',
);
// Print dialog with preview
await InvoiceGenerator.printDocument(
invoice: invoice,
name: 'INV-001',
);
// Inline preview widget
final widget = InvoiceGenerator.preview(
invoice: invoice,
filename: 'INV-001.pdf',
);
GST & Multi-Locale Support #
Indian GST with CGST/SGST/IGST:
final item = LineItem(
description: 'Service',
quantity: 1,
unitPrice: 10000,
cgstPercent: 9, // Central GST
sgstPercent: 9, // State GST
);
Interstate (IGST):
final item = LineItem(
description: 'Service',
quantity: 1,
unitPrice: 10000,
igstPercent: 18, // Integrated GST
);
Multi-locale formatting:
final config = TemplateConfig(
locale: 'en_IN', // Indian numbering: 1,23,456.78
dateFormat: 'dd-MM-yyyy',
currencySymbol: 'โน',
currencyCode: 'INR',
);
// Or use formatters directly
CurrencyFormatter.format(123456.78, locale: 'en_US'); // 123,456.78
CurrencyFormatter.format(123456.78, locale: 'en_IN'); // 1,23,456.78
QR Codes & Barcodes #
Generate UPI QR codes:
final upiLink = QrHelper.generateUpiLink(
upiId: 'acme@okhdfcbank',
payerName: 'Acme Inc',
amount: 5000,
description: 'Invoice INV-001',
); // upi://pay?pa=acme@okhdfcbank&pn=Acme%20Inc&am=5000&tn=Invoice%20INV-001
Or create custom QR codes:
final qr = QrHelper.generateQrCode(
'https://example.com/invoice/INV-001',
width: 100,
height: 100,
);
Examples #
See the /example folder for complete demo apps:
- Home Screen: Gallery of all 5 templates
- Invoice Form: Build invoices from form input
- Template Picker: Switch between templates
- JSON Editor: Edit JSON templates live
- Theming Demo: Color picker and theme customization
- GST Demo: Pre-filled Indian B2B invoice
- POS Demo: Thermal receipt with QR code
Run the example:
cd example
flutter run
API Reference #
Core Classes #
Invoice: Strongly-typed invoice modelInvoiceData: JSON-backed flexible containerInvoiceGenerator: Main public APIInvoiceTemplate: Base class for templatesTemplateConfig: Customization optionsParty: Buyer/seller detailsLineItem: Invoice line itemsTotals: Auto-calculated totals with tax breakdownPayment: Payment informationTax: Tax breakdown (CGST, SGST, IGST, simple tax)
Utility Classes #
CurrencyFormatter: Multi-currency formattingDateFormatter: Localized date formattingNumberFormatter: Locale-specific number separatorsColorUtils: Hex โ PdfColor conversionFontLoader: Google Fonts with Unicode auto-detectionQrHelper: QR code and barcode generationPlaceholderResolver: Template placeholder resolution
Exceptions #
InvoiceDataException: Invalid invoice dataTemplateException: Template build failureJsonTemplateException: Invalid JSON templateRenderingException: PDF generation failurePdfActionException: File I/O failure
Contributing #
Contributions welcome! File issues or open pull requests on GitHub.
License #
MIT License โ see LICENSE file for details.
Support #
For questions, issues, or feature requests, visit:
- ๐ Issue Tracker
- ๐ฌ Discussions
- ๐ง Email: support@example.com