Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Build a Custom Invoicing System in FlutterFlow

Build a complete invoicing system with a Firestore invoices collection storing client references, dynamic line items with quantity and unit price, auto-calculated subtotal, tax, and grand total via a Custom Function with proper decimal rounding. Generate PDF invoices using the pdf Dart package in a Custom Action, upload to Firebase Storage, and send via a Cloud Function email trigger. Track invoice status with color-coded badges for draft, sent, paid, and overdue.

What you'll learn

  • How to model invoices with dynamic line items and auto-incrementing invoice numbers in Firestore
  • How to build a dynamic line item form with add/remove rows and auto-calculated totals
  • How to generate PDF invoices using the pdf Dart package as a Custom Action
  • How to track invoice status with color-coded badges and overdue detection
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner9 min read30-40 minFlutterFlow Pro+ (Custom Actions for PDF generation)March 2026RapidDev Engineering Team
TL;DR

Build a complete invoicing system with a Firestore invoices collection storing client references, dynamic line items with quantity and unit price, auto-calculated subtotal, tax, and grand total via a Custom Function with proper decimal rounding. Generate PDF invoices using the pdf Dart package in a Custom Action, upload to Firebase Storage, and send via a Cloud Function email trigger. Track invoice status with color-coded badges for draft, sent, paid, and overdue.

Invoice generator with line items, PDF export, and status tracking

This tutorial builds a full invoicing system: create invoices with dynamic line items, auto-calculate subtotal, tax, and grand total, generate professional PDF documents, email them to clients, and track payment status. Invoices are stored in Firestore with auto-incrementing numbers and status tracking (draft, sent, paid, overdue). This is about creating and managing payment documents — for Stripe payment processing, see the e-commerce tutorial.

Prerequisites

  • A FlutterFlow project with Firebase/Firestore connected
  • Firebase Storage enabled for PDF uploads
  • Cloud Functions enabled for email delivery (Firebase Blaze plan)
  • Basic familiarity with Page State and Custom Functions in FlutterFlow

Step-by-step guide

1

Design the Firestore invoices and clients collections

Create a clients collection with fields: name (String), email (String), company (String), address (String), and phone (String). Create an invoices collection with fields: invoiceNumber (String — auto-generated like INV-0001), clientId (String), clientName (String — denormalized for display), items (List of Maps — each with description String, qty Integer, unitPrice Double, lineTotal Double), subtotal (Double), taxRate (Double — e.g., 0.08 for 8%), taxAmount (Double), grandTotal (Double), status (String — draft, sent, paid, overdue), dueDate (Timestamp), pdfUrl (String — Firebase Storage URL), createdAt (Timestamp), and notes (String). For auto-increment: use a counters document that stores the last invoice number and increment via transaction on each new invoice.

Expected result: Firestore has clients and invoices collections with a counters document for auto-incrementing invoice numbers.

2

Build the dynamic line item form with auto-calculated totals

Create a CreateInvoicePage with a Page State variable lineItems of type List of JSON (initially one empty row). At the top, add a DropDown bound to a Backend Query on clients, displaying client names, with the selected value stored in Page State selectedClientId. Below, place a ListView bound to lineItems where each row is a Row of four widgets: TextField for description, TextField (numeric keyboard) for quantity, TextField (numeric, decimal) for unit price, and a Text showing the line total (qty * unitPrice). Add an IconButton (Icons.remove_circle) per row to remove it from lineItems. Below the ListView, add a TextButton 'Add Line Item' that appends a new empty row. At the bottom, display three calculated Texts: Subtotal (sum of all line totals), Tax (subtotal * taxRate), and Grand Total (subtotal + tax). Bind these to a Custom Function calculateInvoiceTotals that takes the lineItems list and taxRate, computes each with toStringAsFixed(2) rounding, and returns the values.

Expected result: Users can add and remove line items dynamically. Subtotal, tax, and grand total update automatically as items are entered.

3

Save the invoice with auto-incrementing invoice number

Add a Save Invoice button at the bottom of the form. The On Tap Action Flow first runs a Custom Action that reads the counters/invoiceCounter document, increments the count atomically via a Firestore transaction, and formats the new number as INV-{count.toString().padLeft(4, '0')}. Then create the invoice document in Firestore with all fields: the generated invoiceNumber, selectedClientId, denormalized clientName, the lineItems array (with calculated lineTotal per row), subtotal, taxRate, taxAmount, grandTotal, status: draft, dueDate from a DateTimePicker on the form, empty pdfUrl, current timestamp, and any notes. After creation, navigate to the InvoiceDetailPage passing the new invoice reference.

get_next_invoice_number.dart
1// Custom Action: getNextInvoiceNumber
2final counterRef = FirebaseFirestore.instance
3 .collection('counters').doc('invoiceCounter');
4String invoiceNumber = '';
5await FirebaseFirestore.instance.runTransaction((tx) async {
6 final snap = await tx.get(counterRef);
7 final current = snap.exists ? (snap.data()?['count'] ?? 0) : 0;
8 final next = current + 1;
9 tx.set(counterRef, {'count': next});
10 invoiceNumber = 'INV-${next.toString().padLeft(4, '0')}';
11});
12return invoiceNumber;

Expected result: Each new invoice gets a unique auto-incrementing number (INV-0001, INV-0002, etc.) and is saved to Firestore as a draft.

4

Generate a PDF invoice and upload to Firebase Storage

Create a Custom Action generateInvoicePdf that uses the pdf Dart package (add to pubspec: pdf, printing). The action takes the invoice data as parameters and builds a PDF document: company logo and name at the top, invoice number and date on the right, client billing details below, a table with columns for Description, Qty, Unit Price, and Line Total, followed by subtotal, tax, and grand total rows, and payment instructions at the bottom. Convert the PDF to bytes with document.save(), upload to Firebase Storage at invoices/{invoiceId}/invoice.pdf using firebase_storage, get the download URL, and update the invoice document's pdfUrl field. Add a 'Generate PDF' button on the InvoiceDetailPage that triggers this action and then shows the PDF in a preview using a WebView or Custom Widget.

Expected result: A professional PDF invoice is generated, uploaded to Firebase Storage, and the download URL is saved on the invoice document.

5

Send the invoice via email and track payment status

Deploy a Cloud Function sendInvoiceEmail triggered by an HTTP call that accepts invoiceId, reads the invoice and client data, and sends an email via SendGrid with the PDF attached (downloaded from Storage URL) and a 'Pay Now' link. In FlutterFlow, add a 'Send Invoice' button on the InvoiceDetailPage that calls this Cloud Function and then updates the invoice status to sent. For status tracking, create an InvoicesListPage with a ListView of all invoices ordered by createdAt desc. Each row shows: invoiceNumber, clientName, grandTotal, dueDate, and a status badge — Container with conditional background color: grey for draft, blue for sent, green for paid, red for overdue. Add a Conditional Action to mark as paid: a 'Record Payment' button updates status to paid. For overdue detection, deploy a scheduled Cloud Function that runs daily and updates status to overdue for any sent invoice where dueDate < today.

Expected result: Invoices can be emailed to clients. The invoice list shows color-coded status badges and automatically marks invoices as overdue.

Complete working example

Invoicing System Architecture
1Firestore Data Model:
2 clients/{clientId}
3 name: String ("Acme Corp")
4 email: String ("billing@acme.com")
5 company: String
6 address: String
7 phone: String
8 invoices/{invoiceId}
9 invoiceNumber: String ("INV-0042")
10 clientId: String
11 clientName: String (denormalized)
12 items: List<Map>
13 [{description: "Web Design", qty: 1, unitPrice: 2500.00,
14 lineTotal: 2500.00}, ...]
15 subtotal: Double (3750.00)
16 taxRate: Double (0.08)
17 taxAmount: Double (300.00)
18 grandTotal: Double (4050.00)
19 status: String ("draft"|"sent"|"paid"|"overdue")
20 dueDate: Timestamp
21 pdfUrl: String (Storage download URL)
22 createdAt: Timestamp
23 notes: String
24 counters/invoiceCounter
25 count: Integer (42)
26
27CreateInvoicePage:
28 DropDown (client selector, query: clients)
29 ListView (Page State: lineItems)
30 Row per line item
31 TextField (description)
32 TextField (qty, numeric)
33 TextField (unitPrice, decimal)
34 Text (lineTotal = qty * unitPrice)
35 IconButton (remove row)
36 TextButton ("+ Add Line Item")
37 DateTimePicker (due date)
38 Divider
39 Text: Subtotal = sum(lineTotals)
40 Text: Tax = subtotal * taxRate
41 Text: Grand Total = subtotal + tax
42 Button: Save Invoice transaction for number Create doc
43
44InvoicesListPage:
45 ListView (query: invoices, orderBy createdAt DESC)
46 Row: invoiceNumber + clientName + grandTotal + dueDate
47 + Container badge (grey/blue/green/red by status)

Common mistakes when building a Custom Invoicing System in FlutterFlow

Why it's a problem: Calculating totals client-side only without proper decimal rounding

How to avoid: Use a Custom Function that rounds each line total to 2 decimal places using toStringAsFixed(2) before summing. Calculate subtotal, then taxAmount = (subtotal * taxRate).toStringAsFixed(2), then grandTotal = subtotal + taxAmount. Parse back to double after rounding.

Why it's a problem: Using a simple document count for invoice numbers instead of an atomic counter

How to avoid: Use a dedicated counters document with a Firestore transaction: read the current count, increment by 1, and write back atomically. This guarantees unique sequential numbers even under concurrent access.

Why it's a problem: Storing the PDF only locally without uploading to Firebase Storage

How to avoid: Upload the generated PDF bytes to Firebase Storage at a predictable path (invoices/{invoiceId}/invoice.pdf), save the download URL on the invoice document, and use that URL for email attachments and in-app viewing.

Best practices

  • Round all monetary calculations to 2 decimal places using toStringAsFixed(2) to prevent penny discrepancies
  • Use a Firestore transaction for auto-incrementing invoice numbers to guarantee uniqueness
  • Denormalize clientName on the invoice document to avoid join queries in list views
  • Upload generated PDFs to Firebase Storage and save the URL on the invoice for cross-device access
  • Run a scheduled Cloud Function daily to detect and mark overdue invoices automatically
  • Soft-delete invoices (set status to cancelled) instead of deleting — maintain audit trail
  • Add a notes field for payment terms, bank details, or special instructions per invoice

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

Design a Firestore data model for an invoicing system with clients and invoices collections. Each invoice has dynamic line items with description, quantity, unit price, and line total. Include auto-incrementing invoice numbers via a Firestore transaction counter. Write a Dart function that calculates subtotal, tax, and grand total with proper 2-decimal rounding.

FlutterFlow Prompt

Create an invoice creation page with a client dropdown at the top, a dynamic list of line item rows (each with description, quantity, unit price fields), an Add Line Item button, and auto-calculated subtotal, tax, and grand total at the bottom. Add a Save button that creates the invoice document in Firestore.

Frequently asked questions

How do I auto-number invoices sequentially?

Use a dedicated Firestore document (counters/invoiceCounter) with a count field. Before creating each invoice, run a Firestore transaction that reads the current count, increments it by 1, writes it back, and returns the formatted number (e.g., INV-0042). The transaction guarantees no two invoices get the same number.

Can I add multiple tax rates for different line items?

Yes. Add a taxRate field to each line item map instead of using a single invoice-level taxRate. Calculate taxAmount per line (lineTotal * lineTaxRate) and sum all line taxes for the invoice total. Update the Custom Function to handle per-item tax rates.

How do I generate the PDF in FlutterFlow?

Create a Custom Action using the pdf Dart package. Build a pw.Document() with pw.Page containing your layout: company header, invoice details, a pw.Table for line items, and totals. Call document.save() to get bytes, then upload to Firebase Storage using firebase_storage. This requires FlutterFlow Pro plan for custom code.

How does the overdue detection work?

Deploy a scheduled Cloud Function that runs daily (e.g., every day at midnight). It queries invoices where status == sent AND dueDate < today. For each matching invoice, it updates the status to overdue. The color-coded badge on the invoice list updates automatically.

Can I accept payments directly from the invoice email?

Yes. Include a Pay Now link in the email that opens a Stripe Payment Link or Checkout Session URL. When payment completes, a Stripe webhook Cloud Function updates the invoice status to paid in Firestore.

Can RapidDev help build an invoicing system with recurring invoices and payment processing?

Yes. A production invoicing system with recurring schedules, late payment reminders, multi-currency support, accounting integrations (QuickBooks, Xero), and Stripe payment links requires Cloud Functions and third-party API integrations. RapidDev can build the complete system.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.