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

How to Integrate a Parcel Tracking System in FlutterFlow

Integrate parcel tracking in FlutterFlow by connecting to a carrier-agnostic API such as ShipEngine or AfterShip via a Cloud Function. The Cloud Function fetches normalized tracking data across FedEx, UPS, USPS, and DHL, stores updates in Firestore, and your FlutterFlow app displays the status timeline and estimated delivery date in real time. Poll for updates via a scheduled Cloud Function every 1-2 hours rather than on every user page load.

What you'll learn

  • How to set up a Cloud Function to proxy ShipEngine or AfterShip tracking API calls
  • How to store and display tracking events as a chronological timeline in FlutterFlow
  • How to show a map marker for the current package location using FlutterFlowGoogleMap
  • How to trigger push notifications when a parcel status changes
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner8 min read45-60 minFlutterFlow Free+March 2026RapidDev Engineering Team
TL;DR

Integrate parcel tracking in FlutterFlow by connecting to a carrier-agnostic API such as ShipEngine or AfterShip via a Cloud Function. The Cloud Function fetches normalized tracking data across FedEx, UPS, USPS, and DHL, stores updates in Firestore, and your FlutterFlow app displays the status timeline and estimated delivery date in real time. Poll for updates via a scheduled Cloud Function every 1-2 hours rather than on every user page load.

Multi-carrier parcel tracking for FedEx, UPS, USPS, and DHL in one integration

Parcel tracking connects your FlutterFlow app to the status history of any shipment — in-transit updates, delivery confirmation, exceptions, and estimated delivery dates. Rather than integrating each carrier's API separately (each with different auth, response formats, and rate limits), carrier-agnostic platforms like ShipEngine and AfterShip normalize tracking data into a single consistent format. A Cloud Function proxies these API calls, caches results in Firestore, and a scheduled function polls for status updates. Your FlutterFlow UI then reads from Firestore for instant display without hitting the tracking API on every user page load.

Prerequisites

  • A FlutterFlow project with Firebase configured (Settings → Project Setup → Firebase)
  • A ShipEngine account (shipengine.com) or AfterShip account (aftership.com) — both have free tiers
  • Cloud Functions enabled on your Firebase project (requires Blaze pay-as-you-go billing)
  • Basic familiarity with FlutterFlow's API Manager and Backend Query

Step-by-step guide

1

Create the tracking Cloud Function

In Firebase Console → Functions, create an HTTP-triggered Cloud Function named getTrackingInfo. The function receives a tracking number and optionally a carrier code. It calls the ShipEngine API: GET https://api.shipengine.com/v1/tracking?carrier_code=fedex&tracking_number={number} with the header API-Key: your_shipengine_api_key. ShipEngine returns a normalized response with status_code (AC=Accepted, IT=In Transit, DE=Delivered, EX=Exception, UN=Unknown), status_description, events array (each with occurred_at, description, city, state, country), and estimated_delivery_date. Store the API key in Cloud Function environment variables — never hardcode it. Deploy this function and note its HTTPS URL.

Expected result: The Cloud Function returns normalized tracking data in a consistent JSON structure regardless of which carrier handled the shipment.

2

Store tracking data in Firestore and set up the API Call in FlutterFlow

In Firestore, create a shipments collection with the schema: trackingNumber (String), carrierCode (String), status (String), statusDescription (String), estimatedDelivery (Timestamp), lastUpdated (Timestamp), events (Array of Maps: occurredAt, description, city, state, country), currentLat (Double), currentLng (Double). In FlutterFlow, go to API Manager → Add API Group named TrackingAPI with base URL pointing to your Cloud Function's URL. Add a GET call named trackPackage with a query parameter trackingNumber. When a user submits a tracking number, call this API → on success, write the response data to Firestore shipments/{trackingNumber} using a Backend Call → Create Document or Update Document action.

Expected result: Tracking data is stored in Firestore and can be read instantly by all pages without re-calling the carrier API.

3

Build the tracking timeline UI

Create a TrackingDetail page with a Page Parameter named trackingNumber (String). Add a Backend Query on the page pointing to shipments/{trackingNumber} — this loads the stored tracking data. At the top of the page, add a Container with a status badge: bind its text to statusDescription and its background color to a Conditional Value (green for Delivered, orange for In Transit, red for Exception, grey for Unknown). Below the status, add a Text showing estimatedDelivery formatted as a date. For the event timeline, add a ListView bound to the events array using Generate Dynamic Children. Each child is a Row containing a vertical line decoration, a dot icon, and two Text widgets for the description and occurredAt timestamp. Add a FlutterFlowGoogleMap with a single marker bound to currentLat/currentLng to show the last known package location.

Expected result: The tracking page shows the current status badge, estimated delivery, a chronological event timeline, and a map pin at the last known package location.

4

Set up a scheduled Cloud Function to poll for status updates

Create a second Cloud Function named pollTrackingUpdates triggered by Cloud Scheduler (every 60 minutes). The function queries Firestore for all shipments where status is not Delivered — these are active shipments that need polling. For each, call the ShipEngine tracking API and compare the new status to the stored status. If different, update the Firestore document with the new status, events, and lastUpdated timestamp. This centralized approach means 1,000 users tracking the same shipment results in only one API call per hour, not 1,000. Set the cron schedule to run less frequently during off-peak hours (e.g., every 2 hours between midnight and 6am).

Expected result: Active shipment statuses update automatically in Firestore every hour. All FlutterFlow users see current tracking data without any user-triggered API calls.

5

Add push notifications for status changes

Extend the pollTrackingUpdates Cloud Function to send push notifications when a shipment status changes. When a status change is detected, look up the user's FCM token from their Firestore user document. Use the Firebase Admin SDK: admin.messaging().send({ token: fcmToken, notification: { title: 'Package Update', body: 'Your shipment is now ' + newStatus }, data: { trackingNumber: trackingNumber } }). In FlutterFlow, handle the notification tap — navigate to the TrackingDetail page passing the trackingNumber from the notification data payload. Test by manually updating a shipment's status in Firestore and verifying the notification arrives.

Expected result: Users receive a push notification whenever their shipment status changes — delivery confirmation, exceptions, or entry to their city.

Complete working example

get_tracking_info.js
1const functions = require('firebase-functions');
2const admin = require('firebase-admin');
3const axios = require('axios');
4admin.initializeApp();
5
6const SHIPENGINE_KEY = functions.config().shipengine.key;
7
8exports.getTrackingInfo = functions.https.onRequest(async (req, res) => {
9 res.set('Access-Control-Allow-Origin', '*');
10 if (req.method === 'OPTIONS') return res.status(204).send('');
11
12 const { trackingNumber, carrierCode } = req.query;
13 if (!trackingNumber) return res.status(400).json({ error: 'trackingNumber required' });
14
15 try {
16 const url = `https://api.shipengine.com/v1/tracking`;
17 const params = { tracking_number: trackingNumber };
18 if (carrierCode) params.carrier_code = carrierCode;
19
20 const response = await axios.get(url, {
21 headers: { 'API-Key': SHIPENGINE_KEY },
22 params,
23 });
24
25 const data = response.data;
26 const result = {
27 trackingNumber,
28 status: data.status_code,
29 statusDescription: data.status_description,
30 estimatedDelivery: data.estimated_delivery_date,
31 events: (data.events || []).map((e) => ({
32 occurredAt: e.occurred_at,
33 description: e.description,
34 city: e.city_locality,
35 state: e.state_province,
36 country: e.country_code,
37 })),
38 };
39
40 await admin.firestore()
41 .collection('shipments')
42 .doc(trackingNumber)
43 .set({ ...result, lastUpdated: admin.firestore.FieldValue.serverTimestamp() }, { merge: true });
44
45 res.json(result);
46 } catch (err) {
47 res.status(500).json({ error: err.message });
48 }
49});

Common mistakes

Why it's a problem: Calling the carrier tracking API on every user page load

How to avoid: Use a scheduled Cloud Function to poll active shipments every 60-120 minutes and store results in Firestore. FlutterFlow reads from Firestore, which is instant and nearly free.

Why it's a problem: Registering the FlutterFlow app URL as the carrier webhook endpoint

How to avoid: Register your Cloud Function HTTPS URL as the webhook endpoint in the carrier's dashboard. The Cloud Function receives the webhook, updates Firestore, and notifies users via FCM.

Why it's a problem: Not handling the DELIVERED status to stop polling completed shipments

How to avoid: In your scheduled polling function, filter Firestore to only query shipments where status is not 'DE' (Delivered) or 'EX' (final exception). Mark delivered shipments with a completedAt timestamp.

Best practices

  • Normalize tracking data in the Cloud Function before writing to Firestore — this decouples your FlutterFlow UI from the specific carrier API response format
  • Store carrier-agnostic status codes (IN_TRANSIT, DELIVERED, EXCEPTION) rather than carrier-specific codes so your UI logic stays simple
  • Show the last updated timestamp on the tracking page so users understand how fresh the data is
  • Add a manual refresh button that triggers a new API call for users who need immediate status rather than waiting for the scheduled poll
  • Color-code status badges for instant recognition: green for Delivered, orange for In Transit, blue for Out for Delivery, red for Exception
  • Validate tracking numbers with a regex check before submitting to the API — most carrier formats are well-documented and can be validated client-side
  • Cache the carrier logo or icon alongside the tracking data so the UI can show the appropriate carrier branding without additional API calls

Still stuck?

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

ChatGPT Prompt

I am building a FlutterFlow app with parcel tracking. Write a Firebase Cloud Function in Node.js that accepts a tracking number as a query parameter, calls the ShipEngine API to get normalized tracking data, stores the result in a Firestore 'shipments' collection, and returns the data as JSON. Include proper error handling and CORS headers. Also write a second scheduled Cloud Function that polls all non-delivered shipments every 60 minutes and sends an FCM push notification when the status changes.

FlutterFlow Prompt

Create a tracking detail page in my FlutterFlow app that reads a shipment document from Firestore using a trackingNumber page parameter. Show a status badge at the top colored by status, an estimated delivery date, a vertical timeline ListView of tracking events (each showing timestamp, description, and location), and a Google Map with a marker at the last known location.

Frequently asked questions

Which parcel tracking API should I use for my FlutterFlow app?

ShipEngine is the best choice for apps that also create shipping labels — it handles rate shopping, label generation, and tracking in one API. AfterShip is better for tracking-only use cases with a very generous free tier and excellent webhook support. Both normalize data across FedEx, UPS, USPS, DHL, and 400+ other carriers.

Can I track international shipments including DHL and Canada Post?

Yes. ShipEngine supports 50+ carriers globally. AfterShip supports 900+ carriers. Pass the carrier code (e.g., 'dhl_express', 'canada_post') along with the tracking number. For most international shipments, ShipEngine can auto-detect the carrier from the tracking number format if you omit the carrier code.

How do I let users track a package without logging in?

Store the tracking number in App State (not requiring auth) and allow the Firestore shipments collection to be publicly readable. In your Firestore security rules, add: allow read: if true for the shipments collection. Anyone with the tracking number can read the status. Only your Cloud Function (using admin SDK, which bypasses rules) can write to it.

How often should I poll for tracking updates?

Most carriers update tracking status 2-6 times per day. Polling every 60 minutes is more than sufficient and keeps API costs low. For time-sensitive deliveries (e.g., out for delivery status), you can increase polling to every 15 minutes for shipments in that status specifically.

What if the carrier is not returning location coordinates for the map?

Not all carriers return GPS coordinates in tracking events. Use a geocoding API (Google Maps Geocoding) to convert the city and state from tracking events into coordinates. Pass the city+state string to the Geocoding API in your Cloud Function and store the returned lat/lng in Firestore alongside the event data.

What if I need help building the full order-to-delivery tracking experience?

A production tracking system often requires integrating with an order management system, handling multiple parcels per order, managing webhook security, and building notification preferences. RapidDev can design and implement the complete shipping and tracking architecture for your FlutterFlow app.

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.