FlutterFlow's Custom Code panel (left sidebar) lets you write real Dart code as Custom Actions, Custom Functions, or Custom Widgets. Open the panel, create your item, write Dart in the editor with syntax highlighting, tap Compile to check for errors, then call it from your Action Flows or data bindings. Add Flutter packages in the pubspec.yaml tab and tap Get Packages to install them.
Extending FlutterFlow with Real Dart Code
FlutterFlow's visual builder covers the majority of common app patterns, but sometimes you need logic or UI that no built-in widget provides. The Custom Code panel is where you bridge that gap — writing real Dart/Flutter code that integrates seamlessly with the rest of your visual project. There are three types of custom code: Custom Actions (imperative logic, side effects), Custom Functions (pure transformations used in bindings), and Custom Widgets (UI components returned as Flutter widgets). All three share the same editor with Dart syntax highlighting, auto-import of FlutterFlow helpers, and a Compile button that checks for errors before you use the code in your app.
Prerequisites
- A FlutterFlow project already created (any plan)
- Basic understanding of what actions and widgets are in FlutterFlow
- Familiarity with reading Dart syntax (writing experience not required)
- A Firebase or Supabase backend connected if your custom code needs data access
Step-by-step guide
Open the Custom Code panel
Open the Custom Code panel
In the FlutterFlow editor, look at the left sidebar icons. Click the angle-bracket icon (</>) labeled 'Custom Code' — it is typically near the bottom of the sidebar icon column. The Custom Code panel expands to show three tabs across the top: Custom Actions, Custom Functions, and Custom Widgets. Each tab has a '+' button to create a new item. You can also reach the panel via the main menu: Project Settings → Custom Code. Take a moment to look at the three tabs before proceeding, since choosing the right type up front saves you from having to re-create code in the wrong category.
Expected result: The Custom Code panel is open and you can see the Custom Actions, Custom Functions, and Custom Widgets tabs.
Create a Custom Action and define its parameters
Create a Custom Action and define its parameters
Click the 'Custom Actions' tab, then tap the '+' button to create a new action. Give it a descriptive camelCase name such as 'logAnalyticsEvent'. Once created, the right pane shows the action editor. Scroll down to the 'Parameters' section and tap 'Add Parameter'. Set the name to 'eventName', choose type 'String', and mark it as required. Add a second parameter named 'metadata' with type 'JSON' (optional). Parameters you define here will appear as fillable fields when you add this action to an Action Flow on any button or lifecycle event. FlutterFlow auto-generates the Dart function signature from your parameter definitions — you do not write the signature manually.
Expected result: A new Custom Action named 'logAnalyticsEvent' exists with two defined parameters visible in the Parameters panel.
Write the Dart body and compile
Write the Dart body and compile
The code editor displays the auto-generated function stub with your parameters already wired in. Write your Dart logic inside the function body. FlutterFlow auto-imports common packages — you can reference FirebaseFirestore.instance, FirebaseAuth.instance, and FlutterFlow state variables without adding import statements manually. When you have finished writing, tap the blue 'Compile' button at the top-right of the editor. The panel shows a green 'Compiled successfully' banner if there are no errors, or a red error list with line numbers if there are problems. Fix any errors shown before leaving the editor — uncompiled or error-state custom code cannot be called from Action Flows.
1// Auto-generated stub — write your logic inside2Future logAnalyticsEvent(3 String eventName,4 dynamic metadata,5) async {6 final user = FirebaseAuth.instance.currentUser;7 if (user == null) return;89 await FirebaseFirestore.instance10 .collection('analytics_events')11 .add({12 'event': eventName,13 'metadata': metadata ?? {},14 'uid': user.uid,15 'timestamp': FieldValue.serverTimestamp(),16 });17}Expected result: The Compile button shows a green success banner and no errors are listed.
Create a Custom Function for data transformation
Create a Custom Function for data transformation
Switch to the 'Custom Functions' tab and tap '+'. Name it something like 'formatCurrency'. Custom Functions are pure Dart — they take inputs and return a value, with no side effects or async calls. They are designed to be used in 'Set from Variable' bindings on widget properties, so you can call them directly in Text widget value fields or any binding selector. Define parameters (e.g., amount as double, currencyCode as String) and set the return type to String. Write the transformation logic in the body. Custom Functions cannot access widget state or trigger navigation — if you need those, use a Custom Action instead.
1String formatCurrency(2 double amount,3 String currencyCode,4) {5 final symbols = {'USD': '\$', 'EUR': '€', 'GBP': '£'};6 final symbol = symbols[currencyCode] ?? currencyCode;7 return '$symbol${amount.toStringAsFixed(2)}';8}Expected result: The Custom Function compiles successfully and appears as an option in the 'Set from Variable → Custom Functions' binding selector.
Add a Flutter package via the pubspec.yaml tab
Add a Flutter package via the pubspec.yaml tab
In the Custom Code panel, look for the 'pubspec.yaml' tab (sometimes shown as a small gear or 'Dependencies' label at the top of the panel). Click it to open the dependency editor. You will see the existing FlutterFlow-managed packages listed. Add a new line under 'dependencies' with the package name and version, for example: 'share_plus: ^9.0.0'. After typing the dependency, tap the 'Get Packages' button at the top of the pubspec tab. FlutterFlow runs flutter pub get in the background — wait for the green success indicator before returning to your custom code. You can now import the package at the top of any Custom Action or Custom Widget using the standard Dart import syntax.
Expected result: The package appears in the pubspec.yaml tab without version conflicts and Get Packages completes with a success message.
Call your Custom Action from an Action Flow
Call your Custom Action from an Action Flow
Navigate to any widget that has an action trigger — for example, a Button widget. Open its Action Flow editor by tapping the lightning bolt icon or selecting 'Actions' in the properties panel. Tap '+' to add an action, scroll down the action type list, and select 'Custom Action'. Choose your 'logAnalyticsEvent' action from the dropdown. The editor shows fillable fields for each parameter you defined. Set 'eventName' to a string value (or a variable binding) and set 'metadata' to a JSON value or leave it empty. Tap 'Confirm'. Your custom action is now wired to the button and will execute when the button is tapped.
Expected result: The Action Flow editor shows your Custom Action step with parameters filled in, and the action is listed in the widget's action chain.
Complete working example
1// ─── Custom Action: logAnalyticsEvent ───────────────────────────────────────2// Located in FlutterFlow: Custom Code → Custom Actions → logAnalyticsEvent3// Parameters: eventName (String, required), metadata (JSON, optional)45import 'package:flutter/material.dart';6import 'package:firebase_auth/firebase_auth.dart';7import 'package:cloud_firestore/cloud_firestore.dart';89Future logAnalyticsEvent(10 String eventName,11 dynamic metadata,12) async {13 final user = FirebaseAuth.instance.currentUser;14 if (user == null) {15 debugPrint('logAnalyticsEvent: no authenticated user, skipping.');16 return;17 }1819 try {20 await FirebaseFirestore.instance21 .collection('analytics_events')22 .add({23 'event': eventName,24 'metadata': metadata ?? {},25 'uid': user.uid,26 'timestamp': FieldValue.serverTimestamp(),27 });28 debugPrint('logAnalyticsEvent: logged $eventName for uid=${user.uid}');29 } catch (e) {30 debugPrint('logAnalyticsEvent error: $e');31 }32}3334// ─── Custom Function: formatCurrency ────────────────────────────────────────35// Located in FlutterFlow: Custom Code → Custom Functions → formatCurrency36// Parameters: amount (double), currencyCode (String)37// Return type: String38// Use in: Set from Variable bindings on Text widgets3940String formatCurrency(41 double amount,42 String currencyCode,43) {44 final symbols = {45 'USD': '\$',46 'EUR': '€',47 'GBP': '£',48 'JPY': '¥',49 'CAD': 'CA\$',50 };51 final symbol = symbols[currencyCode] ?? currencyCode;52 // JPY has no decimal places53 if (currencyCode == 'JPY') {54 return '$symbol${amount.toInt()}';55 }56 return '$symbol${amount.toStringAsFixed(2)}';57}5859// ─── pubspec.yaml additions (Custom Code → pubspec.yaml tab) ─────────────────60// dependencies:61// share_plus: ^9.0.062// intl: ^0.19.063//64// After adding, tap "Get Packages" in the pubspec.yaml tab.Common mistakes
Why it's a problem: Adding a package in pubspec.yaml but forgetting to tap 'Get Packages'
How to avoid: After adding the dependency line, always tap the 'Get Packages' button at the top of the pubspec.yaml tab and wait for the success indicator before writing any code that imports the new package.
Why it's a problem: Using a Custom Function for code that has side effects or async calls
How to avoid: If your logic needs await, writes to a database, or changes app state, use a Custom Action instead. Reserve Custom Functions purely for data transformation and formatting.
Why it's a problem: Writing the Dart function signature manually instead of using the Parameters panel
How to avoid: Always define parameters using the Parameters panel UI. Only write code inside the function body that FlutterFlow provides — never modify the signature line directly.
Why it's a problem: Leaving a Custom Action in an error state and trying to call it from an Action Flow
How to avoid: Tap Compile after every change and resolve all errors shown before navigating away from the Custom Code panel.
Best practices
- Use descriptive camelCase names for Custom Actions and Functions — they appear in dropdown lists throughout FlutterFlow and vague names like 'myAction1' become confusing quickly.
- Keep Custom Actions focused on a single responsibility. A 'sendEmailAndLogEvent' action is harder to reuse than separate 'sendEmail' and 'logEvent' actions.
- Always use debugPrint() instead of print() for any logging inside Custom Actions — it is stripped in release builds and will not appear in production logs.
- Define all external data your action needs as parameters rather than hardcoding values. This makes the action reusable across multiple screens and data contexts.
- Compile your code after every meaningful edit — do not wait until you have written 100 lines to discover you have a typo on line 5.
- Add a short comment at the top of each Custom Action describing what it does, its expected parameters, and any side effects. Future-you will be grateful.
- When adding packages, pin a specific minor version (e.g., share_plus: ^9.0.0) rather than using 'any'. This prevents unexpected breaking changes when FlutterFlow upgrades its own dependencies.
- Test Custom Actions using FlutterFlow's Run Mode with a real device or emulator rather than only in the visual canvas, since Dart code does not execute in the canvas preview.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I'm building a FlutterFlow app and need to write a Custom Action in Dart. The action should [describe what you want]. It will receive these parameters: [list parameters and types]. It needs to interact with [Firebase / a REST API / device hardware]. Please write the Dart function body only (not the signature) and explain any packages I need to add in pubspec.yaml.
Write a FlutterFlow Custom Action called [actionName] that [describe behavior]. Parameters: [name: type, name: type]. The action should use FlutterFlow's auto-injected Firebase helpers. Return the function body only — I will paste it into the Custom Code editor. Also tell me if I need to add any packages to pubspec.yaml.
Frequently asked questions
What is the difference between a Custom Action and a Custom Function in FlutterFlow?
Custom Actions are async Dart functions with side effects — they can write to databases, call APIs, and trigger navigation. They are used in Action Flows attached to button taps, page load events, and other triggers. Custom Functions are pure synchronous Dart with no side effects — they transform an input into an output and are used in 'Set from Variable' data bindings on widget properties.
Do I need to know Flutter or Dart to use the Custom Code panel?
Basic familiarity with Dart syntax helps, but you do not need deep Flutter experience. The editor provides syntax highlighting and error messages with line numbers. For most common tasks, you can describe what you need to an AI assistant and paste the resulting function body into the editor, then compile to check for issues.
Can I import any Flutter package into a FlutterFlow Custom Action?
You can import most packages from pub.dev, but some packages with native platform code require additional setup steps that FlutterFlow's managed build system may not support. Packages that are purely Dart (no native bindings) always work. For packages with native code like local_auth or camera, FlutterFlow generally supports them but you should test on a real device via Run Mode.
Where do I find the pubspec.yaml tab to add packages?
In the Custom Code panel, look for a tab or button labeled 'pubspec.yaml' or 'Dependencies' near the top of the panel. It may appear as a small icon. If you do not see it, make sure you are in the Custom Code panel (</> icon in the left sidebar) and not inside the widget properties panel.
What happens if my Custom Action has a runtime error when a user triggers it?
The action will throw an uncaught exception, which may cause the app to show an error state or silently fail depending on how FlutterFlow handles the action result. Wrap your Dart code in a try/catch block and use debugPrint to log errors. In Run Mode, errors appear in the DevTools console so you can diagnose them.
Can I call a Custom Action from another Custom Action?
Not directly from the FlutterFlow Action Flow editor — each Action Flow step calls one action at a time. However, inside your Dart code you can call another Dart function you have defined in the same file. For shared utility logic, consider using a Custom Function (if pure) or extracting shared logic into a helper function defined within your Custom Action file.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation