Integrate FlutterFlow with Facebook Ads using the Meta Marketing API via Cloud Functions. Add the facebook_app_events package as a Custom Action to log Purchase, AddToCart, and CompleteRegistration events for pixel tracking. A Cloud Function fetches campaign insights from /act_{adAccountId}/insights and stores spend, impressions, clicks, and CPA in a Firestore ad_metrics collection for your dashboard.
Meta Marketing API attribution and campaign metrics in FlutterFlow
Running paid Facebook and Instagram ads means you need two things: pixel event tracking so Meta knows which app events (purchases, sign-ups) came from your ads, and a dashboard to monitor campaign performance. This tutorial covers both. You will add the facebook_app_events Flutter package via Custom Actions to fire pixel events from within your FlutterFlow app, then build a Cloud Function that calls the Meta Marketing API to fetch campaign insights and cache them in Firestore. Finally, you will build a simple KPI dashboard page with spend, impressions, clicks, and cost-per-action cards plus a ChoiceChips date range filter.
Prerequisites
- A FlutterFlow project with Firebase/Firestore connected
- A Meta Business account with an active ad account
- A Facebook App created at developers.facebook.com with App ID and App Secret
- A Marketing API access token with ads_read permission
- Basic understanding of Cloud Functions and FlutterFlow Custom Actions
Step-by-step guide
Add the facebook_app_events package and configure App ID
Add the facebook_app_events package and configure App ID
In FlutterFlow, open Custom Code → Pubspec Dependencies and add facebook_app_events: ^4.3.1. Then go to Settings → App Details → scroll to iOS and Android configuration sections. Enter your Facebook App ID in the Facebook App ID field — FlutterFlow injects this into AndroidManifest.xml and Info.plist automatically. On iOS, also add your Facebook Client Token (found in Facebook App Dashboard → Settings → Advanced). Save and click Compile Code to verify the package compiles without errors. The App ID links your app events to the correct Facebook pixel for attribution.
Expected result: facebook_app_events compiles successfully and the App ID is wired into the native app configuration.
Create Custom Actions to log pixel events
Create Custom Actions to log pixel events
Go to Custom Code → Custom Actions and create a new action named logFacebookEvent. Add a String parameter named eventName and a Map<String, dynamic> parameter named parameters. In the Dart body: import 'package:facebook_app_events/facebook_app_events.dart'; then write final fbEvents = FacebookAppEvents(); await fbEvents.logEvent(name: eventName, parameters: parameters);. Create specific wrapper actions for your key events: logPurchase (logs FacebookAppEvents.EVENT_NAME_PURCHASED with value_to_sum and currency parameters), logAddToCart (EVENT_NAME_ADDED_TO_CART with content_id and currency), and logCompleteRegistration (EVENT_NAME_COMPLETED_REGISTRATION). In your Action Flows, call these Custom Actions after the corresponding user actions — place logPurchase in the flow that runs after a successful payment confirmation.
1// Custom Action: logPurchase2import 'package:facebook_app_events/facebook_app_events.dart';34Future logPurchase(5 double purchaseAmount,6 String currency,7 String contentId,8) async {9 final fbEvents = FacebookAppEvents();10 await fbEvents.logPurchase(11 amount: purchaseAmount,12 currency: currency,13 parameters: {14 FacebookAppEvents.PARAM_CONTENT_ID: contentId,15 },16 );17}1819// Custom Action: logAddToCart20Future logAddToCart(21 String contentId,22 String contentType,23 double price,24 String currency,25) async {26 final fbEvents = FacebookAppEvents();27 await fbEvents.logEvent(28 name: FacebookAppEvents.EVENT_NAME_ADDED_TO_CART,29 parameters: {30 FacebookAppEvents.PARAM_CONTENT_ID: contentId,31 FacebookAppEvents.PARAM_CONTENT_TYPE: contentType,32 FacebookAppEvents.PARAM_CURRENCY: currency,33 FacebookAppEvents.PARAM_VALUE_TO_SUM: price,34 },35 );36}Expected result: Pixel events appear in Facebook Events Manager → Test Events within minutes of being triggered in the app.
Build a Cloud Function to fetch campaign insights from Marketing API
Build a Cloud Function to fetch campaign insights from Marketing API
In Firebase Console, create a Cloud Function named fetchAdMetrics using Node.js 20. The function calls the Meta Marketing API endpoint GET https://graph.facebook.com/v19.0/act_{adAccountId}/insights with query parameters: fields=campaign_name,spend,impressions,clicks,cpc,cpm,actions&date_preset=last_7d&level=campaign and access_token from an environment variable (never hardcode). Parse the JSON response and for each campaign in the data array, write a document to the Firestore ad_metrics collection with fields: campaignName, spend (parseFloat), impressions (parseInt), clicks (parseInt), cpc (parseFloat), cpm (parseFloat), conversions (extract from actions array where action_type is purchase or complete_registration), and lastUpdated (server timestamp). Schedule this function to run every 30 minutes using Cloud Scheduler. Store your ad account ID and access token in Cloud Function environment variables.
1// Cloud Function: fetchAdMetrics (Node.js 20)2const functions = require('firebase-functions');3const admin = require('firebase-admin');4const axios = require('axios');56exports.fetchAdMetrics = functions.pubsub7 .schedule('every 30 minutes')8 .onRun(async (context) => {9 const adAccountId = process.env.FB_AD_ACCOUNT_ID;10 const accessToken = process.env.FB_ACCESS_TOKEN;1112 const url = `https://graph.facebook.com/v19.0/act_${adAccountId}/insights`;13 const params = {14 fields: 'campaign_name,spend,impressions,clicks,cpc,cpm,actions',15 date_preset: 'last_7d',16 level: 'campaign',17 access_token: accessToken,18 };1920 const response = await axios.get(url, { params });21 const campaigns = response.data.data;22 const db = admin.firestore();23 const batch = db.batch();2425 for (const campaign of campaigns) {26 const conversions = (campaign.actions || [])27 .filter(a => ['purchase', 'complete_registration'].includes(a.action_type))28 .reduce((sum, a) => sum + parseInt(a.value), 0);2930 const ref = db.collection('ad_metrics').doc(campaign.campaign_id);31 batch.set(ref, {32 campaignName: campaign.campaign_name,33 spend: parseFloat(campaign.spend || 0),34 impressions: parseInt(campaign.impressions || 0),35 clicks: parseInt(campaign.clicks || 0),36 cpc: parseFloat(campaign.cpc || 0),37 cpm: parseFloat(campaign.cpm || 0),38 conversions,39 cpa: conversions > 0 ? parseFloat(campaign.spend) / conversions : 0,40 lastUpdated: admin.firestore.FieldValue.serverTimestamp(),41 }, { merge: true });42 }4344 await batch.commit();45 return null;46 });Expected result: Firestore ad_metrics collection populates with campaign data every 30 minutes.
Build the campaign KPI dashboard page
Build the campaign KPI dashboard page
Create a new page named AdDashboard. Add a ChoiceChips widget at the top bound to a Page State variable named selectedDateRange (options: Last 7 Days, Last 14 Days, Last 30 Days). Add a Backend Query on the page: collection ad_metrics, no filter (show all campaigns), order by spend descending. Below the ChoiceChips, add a Row of four Container KPI cards: Total Spend (sum across all results using a Custom Function), Total Impressions, Total Clicks, and Average CPA. Use Conditional Value on each Container to display 'Loading...' while the Backend Query is loading. Below the KPI row, add a ListView bound to the Backend Query result. Each list item is a Component with: campaign name Text (bold), Row with spend/impressions/clicks/conversions Texts, and a small Container showing CPA in green (if CPA < target) or red. When selectedDateRange changes, call a Custom Action that re-triggers the Cloud Function with the new date_preset parameter.
Expected result: The dashboard displays live campaign metrics with four KPI cards and a sortable campaign list that updates every 30 minutes.
Add a date range filter using ChoiceChips and Custom Action
Add a date range filter using ChoiceChips and Custom Action
To make the date range filter functional, create a Custom Action named refreshAdMetrics that accepts a String parameter datePreset. The action calls your Cloud Function via an HTTP POST using FlutterFlow's API Call feature or FFAppState-triggered re-query. In FlutterFlow's API Manager, add an API Group named AdMetricsAPI with base URL pointing to your Cloud Function HTTPS trigger URL. Add a GET endpoint refreshMetrics with query parameter date_preset. In the ChoiceChips On Value Selected Action Flow: first update the Page State selectedDateRange variable, then call the refreshMetrics API endpoint passing the selected date preset (map 'Last 7 Days' to 'last_7d', 'Last 14 Days' to 'last_14d', 'Last 30 Days' to 'last_30d'). After the API call completes, trigger a page refresh to reload the Backend Query with the updated Firestore data.
Expected result: Selecting a different date range triggers the Cloud Function to re-fetch insights and the dashboard updates within seconds.
Test pixel events in Facebook Events Manager
Test pixel events in Facebook Events Manager
Open Facebook Events Manager → your Pixel → Test Events tab. Enter your app's test URL or use a physical device with the FlutterFlow debug build. In your FlutterFlow app, trigger the actions that call your Custom pixel event actions (add an item to cart, complete a registration, make a test purchase). Within 1-2 minutes, the Test Events panel should show the received events with parameter details. Verify the event names match exactly: Purchase, AddToCart, CompleteRegistration (Facebook is case-sensitive). Also check that the currency and value parameters appear correctly. Once confirmed, switch from test events to the live Events panel — events from real users will appear with a short delay.
Expected result: All three pixel events appear in Facebook Events Manager with correct parameters, confirming attribution is working.
Complete working example
1Pixel Event Tracking (client-side):2├── Custom Action: logPurchase3│ └── facebook_app_events.logPurchase(amount, currency, contentId)4├── Custom Action: logAddToCart5│ └── fbEvents.logEvent(EVENT_NAME_ADDED_TO_CART, parameters)6└── Custom Action: logCompleteRegistration7 └── fbEvents.logEvent(EVENT_NAME_COMPLETED_REGISTRATION)89App ID Configuration:10└── Settings → App Details → Facebook App ID11 └── Auto-injected into AndroidManifest.xml + Info.plist1213Marketing API (server-side only):14└── Cloud Function: fetchAdMetrics (runs every 30 min)15 ├── GET graph.facebook.com/v19.0/act_{adAccountId}/insights16 │ ├── fields: campaign_name,spend,impressions,clicks,cpc,cpm,actions17 │ ├── date_preset: last_7d (or parameterized)18 │ └── access_token: from environment variable (NEVER in client)19 └── Writes to Firestore: ad_metrics/{campaignId}20 ├── campaignName: String21 ├── spend: Double22 ├── impressions: Integer23 ├── clicks: Integer24 ├── cpc: Double25 ├── cpm: Double26 ├── conversions: Integer (from actions array)27 ├── cpa: Double (spend / conversions)28 └── lastUpdated: Timestamp2930AdDashboard Page:31├── ChoiceChips (selectedDateRange: Page State)32│ └── On Value Selected → refreshMetrics API Call → page refresh33├── Row (KPI cards)34│ ├── Container: Total Spend (Custom Function sum)35│ ├── Container: Total Impressions (Custom Function sum)36│ ├── Container: Total Clicks (Custom Function sum)37│ └── Container: Avg CPA (Custom Function avg)38└── ListView (Backend Query: ad_metrics, order by spend DESC)39 └── CampaignRow Component40 ├── Text: campaignName (bold)41 ├── Row: spend | impressions | clicks | conversions42 └── Container: CPA (green if < target, red if > target)Common mistakes
Why it's a problem: Putting the Facebook App Secret in client-side API headers
How to avoid: Store the App Secret and Marketing API access token exclusively in Cloud Function environment variables. The client only uses the App ID (which is public) and the facebook_app_events SDK. All Marketing API calls must go through your Cloud Function.
Why it's a problem: Logging pixel events before the Facebook SDK is fully initialized
How to avoid: Call FacebookAppEvents().setAutoLogAppEventsEnabled(true) in your app initialization Custom Action, and only call logEvent in response to user interactions (button taps, form submissions), not during widget build.
Why it's a problem: Calling the Marketing API /insights endpoint directly from FlutterFlow's API Manager
How to avoid: Route all Marketing API calls through a Cloud Function. The function stores the long-lived system user access token in environment variables and handles token validation server-side.
Best practices
- Always use the Storefront Access Token for pixel events and keep the App Secret exclusively in Cloud Function environment variables
- Use Facebook's Test Events panel in Events Manager to verify pixel events before going live — saves hours of debugging attribution issues
- Log pixel events at the action level, not at the page view level — Facebook optimizes campaigns based on conversion events, not views
- Cache campaign insights in Firestore with a 30-minute TTL using Cloud Scheduler — the Marketing API has rate limits and real-time calls are unnecessary
- Add a conversions field by parsing the actions array from the insights response, not just clicks — CPA calculation requires actual conversion count
- Use date_preset parameters (last_7d, last_14d, last_30d) rather than fixed date ranges so the dashboard always shows rolling windows
- Create a dedicated Facebook system user in Meta Business Manager for your Cloud Function's access token — easier to revoke and audit than personal account tokens
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I am building a FlutterFlow app and want to integrate Facebook Ads pixel tracking. I need to log Purchase, AddToCart, and CompleteRegistration events using the facebook_app_events Flutter package. Write me a Dart Custom Action for each event with the correct event name constants and parameters. Also write a Firebase Cloud Function in Node.js that calls the Meta Marketing API /act_{adAccountId}/insights endpoint and stores campaign metrics (spend, impressions, clicks, CPA) in Firestore.
Create an ad metrics dashboard page with four KPI cards (Total Spend, Total Impressions, Total Clicks, Average CPA) and a ListView of campaigns below. Connect the ListView to my Firestore ad_metrics collection ordered by spend descending. Add ChoiceChips at the top for Last 7 Days, Last 14 Days, and Last 30 Days date range selection.
Frequently asked questions
What Facebook permissions do I need for the Marketing API?
Your access token needs the ads_read permission to fetch campaign insights via /insights. If you also want to create or modify campaigns from your app, add ads_management. Generate the token via a Facebook System User in Meta Business Manager — do not use a personal account token, which expires and creates compliance issues.
How long does it take for pixel events to appear in Events Manager?
Test events in the Test Events tab appear within 1-2 minutes. Live events in the main Events view have a processing delay of up to 20 minutes. For attribution data in Ads Manager, allow up to 24 hours for the full attribution window to process.
Can I track events on iOS 14+ with App Tracking Transparency?
Yes, but you must implement the App Tracking Transparency prompt. The facebook_app_events package has a requestTrackingAuthorization() method. Call it before logging events on iOS. Users who deny tracking will still generate aggregated, anonymized signal via Apple's SKAdNetwork, but individual-level attribution is limited.
What is the difference between a Facebook Pixel and the Marketing API?
The Facebook Pixel (via facebook_app_events) tracks user actions in your app and sends them to Meta for ad attribution and optimization — it tells Meta which ad led to which action. The Marketing API is a separate read interface for pulling your campaign performance data (spend, reach, CPA) into your own dashboard. You need both for a complete setup.
How do I handle the Marketing API rate limit?
The Marketing API uses a score-based rate limit — each account starts with 100 score points that regenerate over time. Fetching insights for 10 campaigns costs fewer points than fetching for 1000. Caching results in Firestore and running your Cloud Function every 30 minutes rather than on-demand keeps you well within limits for most ad account sizes.
Can RapidDev help set up a full Meta Ads attribution pipeline?
Yes. A production-grade Meta attribution setup with server-side Conversions API (CAPI), deduplication between pixel and CAPI events, ATT consent management, and custom audience syncing from Firestore requires backend engineering beyond the visual builder. RapidDev can build the full pipeline.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation