Integrate tax calculations using a Firestore tax_rates collection storing rates by country, state, and city. A Cloud Function looks up the buyer's address at checkout, finds the matching rate, and calculates tax as subtotal times rate. Display the breakdown in the order summary. For complex needs, integrate the TaxJar or Avalara API. Support international types: US sales tax, EU VAT, and AU/IN GST with different calculation rules.
Adding Tax Calculations to Your FlutterFlow Financial App
Every e-commerce or financial app needs tax calculations. Hardcoding rates breaks when jurisdictions change their rates. This tutorial builds a flexible tax engine with Firestore-stored rates that can be updated without redeploying, Cloud Function calculations for accuracy, and support for multiple international tax systems.
Prerequisites
- FlutterFlow project with a checkout or order flow
- Firestore configured for storing tax rates
- Cloud Functions enabled for server-side calculations
- Basic understanding of sales tax, VAT, or GST concepts
Step-by-step guide
Create the Firestore tax rates collection
Create the Firestore tax rates collection
Create a tax_rates collection with fields: country (String, e.g., 'US', 'GB', 'AU'), state (String, e.g., 'CA', 'NY', optional), city (String, optional for city-level overrides), rate (double, e.g., 0.0825 for 8.25%), type (String: 'sales_tax', 'vat', 'gst'), name (String, display label like 'California Sales Tax'), effectiveDate (Timestamp), isActive (bool). Populate with your relevant tax rates. The hierarchical structure (country > state > city) allows the Cloud Function to find the most specific matching rate. For US sales tax, many states have state-level plus city-level rates that combine.
Expected result: Firestore has a tax_rates collection with rates organized by country, state, and city jurisdiction.
Build the Cloud Function for tax calculation at checkout
Build the Cloud Function for tax calculation at checkout
Deploy a Cloud Function called calculateTax that receives the buyer's address (country, state, city) and the order subtotal. The function queries the tax_rates collection to find matching active rates. It first looks for a city-specific rate, then falls back to state, then country. If multiple rates apply (e.g., state + city), it combines them. The function calculates taxAmount = subtotal * combinedRate, rounds to two decimal places, and returns the taxAmount, effective rate, rate name, and breakdown by jurisdiction. This server-side calculation prevents client-side tampering and ensures consistency.
1// Cloud Function: calculateTax2const functions = require('firebase-functions');3const admin = require('firebase-admin');4admin.initializeApp();56exports.calculateTax = functions.https7 .onCall(async (data, context) => {8 const { country, state, city, subtotal } = data;9 const db = admin.firestore();10 11 // Query matching tax rates (most specific first)12 const rates = [];13 14 // City-level rate15 if (city) {16 const citySnap = await db.collection('tax_rates')17 .where('country', '==', country)18 .where('state', '==', state)19 .where('city', '==', city)20 .where('isActive', '==', true)21 .limit(1).get();22 if (!citySnap.empty) rates.push(citySnap.docs[0].data());23 }24 25 // State-level rate26 if (state) {27 const stateSnap = await db.collection('tax_rates')28 .where('country', '==', country)29 .where('state', '==', state)30 .where('city', '==', '')31 .where('isActive', '==', true)32 .limit(1).get();33 if (!stateSnap.empty) rates.push(stateSnap.docs[0].data());34 }35 36 // Country-level rate (VAT/GST)37 const countrySnap = await db.collection('tax_rates')38 .where('country', '==', country)39 .where('state', '==', '')40 .where('city', '==', '')41 .where('isActive', '==', true)42 .limit(1).get();43 if (!countrySnap.empty) rates.push(countrySnap.docs[0].data());44 45 // Calculate combined tax46 let combinedRate = 0;47 const breakdown = rates.map(r => {48 combinedRate += r.rate;49 return {50 name: r.name,51 rate: r.rate,52 amount: parseFloat((subtotal * r.rate).toFixed(2)),53 };54 });55 56 const taxAmount = parseFloat((subtotal * combinedRate).toFixed(2));57 58 return {59 taxAmount,60 combinedRate,61 breakdown,62 total: parseFloat((subtotal + taxAmount).toFixed(2)),63 };64 });Expected result: The Cloud Function finds matching tax rates by jurisdiction and returns the calculated tax amount with a breakdown.
Display the tax breakdown in the order summary
Display the tax breakdown in the order summary
On your checkout or order summary page, after the user enters their shipping address, call the calculateTax Cloud Function with the address fields and cart subtotal. Display the results in a Column: Subtotal row showing the pre-tax amount, then a row for each jurisdiction in the breakdown (e.g., 'California State Tax (7.25%): $14.50' and 'Los Angeles City Tax (1.00%): $2.00'), then a Divider, then the Total row in bold. If the Cloud Function returns an empty breakdown (no matching rates), show 'Tax: $0.00' with a note that tax may be calculated at a later stage. Update the tax display whenever the shipping address changes.
Expected result: The order summary shows subtotal, itemized tax by jurisdiction, and total after the shipping address is entered.
Add support for VAT and GST international tax types
Add support for VAT and GST international tax types
For EU VAT: store country-level rates (e.g., UK 20%, Germany 19%, France 20%) with type 'vat'. VAT is typically included in the displayed price (tax-inclusive) rather than added on top. Add a Custom Function that calculates VAT-inclusive display: if tax type is VAT, show the price as-is and display 'Includes VAT' with the amount extracted (price - price / (1 + rate)). For Australian GST (10%): similar to VAT, typically inclusive. For US sales tax: always exclusive (added at checkout). Use the type field from the tax rate to determine whether to show inclusive or exclusive tax treatment in the UI.
Expected result: The app correctly handles tax-inclusive (VAT, GST) and tax-exclusive (US sales tax) display modes.
Build an admin page for managing tax rates
Build an admin page for managing tax rates
Create a TaxRatesAdmin page accessible only to admin users. Add a ListView querying the tax_rates collection ordered by country, then state. Each list item shows: country flag or code, state name, city name (if applicable), rate percentage, type badge (sales_tax/vat/gst), and active status toggle. Add an Add Rate button that opens a form with DropDowns for country and state, a TextField for city, a TextField for rate, a DropDown for type, and a Switch for isActive. Admin can edit or deactivate rates without redeploying the app. When a rate changes, all future checkouts automatically use the updated rate since the Cloud Function reads from Firestore at runtime.
Expected result: Admins can add, edit, activate, and deactivate tax rates from a management page without app redeployment.
Complete working example
1FIRESTORE SCHEMA:2 tax_rates (collection):3 country: String (ISO code: US, GB, AU, DE)4 state: String (state code or '' for country-level)5 city: String (city name or '' for state-level)6 rate: double (decimal: 0.0825 = 8.25%)7 type: String (sales_tax | vat | gst)8 name: String (display label)9 effectiveDate: Timestamp10 isActive: bool1112CLOUD FUNCTION: calculateTax13 Input: country, state, city, subtotal14 → Query tax_rates: city → state → country (most specific first)15 → Combine applicable rates16 → Calculate: taxAmount = subtotal * combinedRate17 → Round to 2 decimal places18 → Return: { taxAmount, combinedRate, breakdown[], total }1920CHECKOUT PAGE — Tax Display:21 After shipping address entered:22 Call calculateTax(address, subtotal)23 Display:24 Row: Subtotal — $X.XX25 Row per jurisdiction: Tax name (rate%) — $X.XX26 Divider27 Row: Total — $X.XX (bold)2829 VAT/GST mode (tax-inclusive):30 Show product prices with tax included31 Display: "Includes {taxType} of $X.XX"3233 Sales tax mode (tax-exclusive):34 Show product prices without tax35 Add tax at checkout3637PAGE: TaxRatesAdmin38 ListView: tax_rates orderBy country, state39 Each row: country + state + city + rate% + type badge + active toggle40 Button "Add Rate" → form:41 DropDown: country42 DropDown: state (filtered by country)43 TextField: city (optional)44 TextField: rate (decimal)45 DropDown: type (sales_tax/vat/gst)46 Switch: isActiveCommon mistakes
Why it's a problem: Hardcoding tax rates in the app code or Custom Functions
How to avoid: Store all tax rates in Firestore so they can be updated by admins at any time. The Cloud Function reads current rates from Firestore on every calculation.
Why it's a problem: Calculating tax on the client side only
How to avoid: Always calculate tax in a Cloud Function server-side. The client displays the result but the authoritative calculation happens on the server before creating the payment.
Why it's a problem: Not rounding tax amounts to two decimal places
How to avoid: Round every tax amount to two decimal places using toFixed(2) and parseFloat. Apply rounding at the individual jurisdiction level and at the total level.
Best practices
- Store tax rates in Firestore for admin-updatable rates without redeployment
- Calculate tax server-side in Cloud Functions to prevent client manipulation
- Round all monetary amounts to two decimal places at every calculation step
- Support hierarchical rate lookup (country > state > city) for jurisdiction specificity
- Display tax breakdown by jurisdiction for transparency
- Handle both tax-inclusive (VAT/GST) and tax-exclusive (sales tax) modes
- Store an effectiveDate on rates to handle future rate changes proactively
- Log tax calculations on orders for audit and compliance purposes
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Build a tax calculation engine for a FlutterFlow financial app. Store tax rates in Firestore by country/state/city with support for sales tax, VAT, and GST. Create a Cloud Function that looks up the buyer's address, finds matching rates, calculates tax, and returns a breakdown. Show the tax breakdown in the checkout order summary. Include the Cloud Function code and admin page for rate management.
Create a checkout order summary section with rows for Subtotal, Tax (showing the jurisdiction name and rate percentage), a Divider, and a bold Total row. Add a note below saying Includes VAT when the tax type is VAT.
Frequently asked questions
Should I use a tax API like TaxJar instead of my own rate table?
For US businesses selling across many states, yes. TaxJar and Avalara maintain up-to-date rates for every US jurisdiction (10,000+ tax districts). Integrate via Cloud Function: call the API with the address and product category, receive the exact rate. For simple single-jurisdiction or international apps, a Firestore table is sufficient.
How do I handle tax-exempt customers?
Add a taxExempt boolean and exemptionCertificate field on the user document. In the calculateTax Cloud Function, check if the user is tax-exempt before calculating. Return zero tax with a note explaining the exemption.
Are digital products taxed differently?
In many US states and EU countries, yes. Digital products have different tax rules than physical goods. Add a productType parameter to calculateTax and store separate rates or flags for digital vs physical products in your tax_rates collection.
How often do tax rates change?
US state rates typically change once or twice a year. City rates change more frequently. EU VAT rates change less often but temporary rates (like COVID reductions) can be introduced at any time. Review and update your rates quarterly at minimum.
Do I need to collect and remit tax?
Tax collection obligations depend on your business location, customer locations, and revenue thresholds (nexus). This tutorial handles the calculation; consult a tax professional or service like TaxJar for compliance obligations specific to your business.
Can RapidDev help build financial and tax features?
Yes. RapidDev can integrate tax calculation APIs, build multi-jurisdiction tax engines, set up tax reporting dashboards, and connect your FlutterFlow app with accounting systems for automated tax compliance.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation