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

How to Create Custom Code in FlutterFlow

FlutterFlow supports three types of custom Dart code: Custom Actions (async logic like API calls), Custom Functions (synchronous data transformation), and Custom Widgets (full Flutter UI components). Access them all from the left sidebar under Custom Code. Each type serves a different purpose — choosing the right one saves hours of debugging.

What you'll learn

  • The difference between Custom Actions, Custom Functions, and Custom Widgets
  • How to write and register a Custom Action for async operations
  • How to write a synchronous Custom Function for data transformation
  • How to build a Custom Widget that renders Flutter UI
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner12 min read20-30 minFlutterFlow Pro+ (code export recommended for testing)March 2026RapidDev Engineering Team
TL;DR

FlutterFlow supports three types of custom Dart code: Custom Actions (async logic like API calls), Custom Functions (synchronous data transformation), and Custom Widgets (full Flutter UI components). Access them all from the left sidebar under Custom Code. Each type serves a different purpose — choosing the right one saves hours of debugging.

Extending FlutterFlow with Dart Code

FlutterFlow's visual builder covers most app needs, but sometimes you need logic or UI that the drag-and-drop editor cannot express. Custom Code lets you drop into raw Dart/Flutter for exactly those cases. There are three distinct types, each with its own access pattern and constraints. Understanding which type to use — and why — is the key skill this tutorial teaches.

Prerequisites

  • A FlutterFlow project already created (Free plan works for writing; Pro for code export)
  • Basic familiarity with FlutterFlow's widget canvas
  • No prior Dart experience required, but helpful
  • Understanding of what Actions and Functions mean in FlutterFlow's visual builder

Step-by-step guide

1

Navigate to Custom Code and choose your code type

In the FlutterFlow editor, look at the far left sidebar. You will see icons for Pages, Components, and several others. Click the icon that looks like angle brackets (</>) — this opens the Custom Code panel. Inside, you will see three sections: Custom Actions, Custom Functions, and Custom Widgets. Each has a '+' button to create a new item. Take a moment to read the brief description FlutterFlow shows for each type before creating anything. This orientation step prevents the most common mistake: creating a Custom Action when you actually need a Custom Function.

Expected result: The Custom Code panel is open showing three sections: Custom Actions, Custom Functions, and Custom Widgets.

2

Create a Custom Action for Async Logic

Click '+' next to Custom Actions. Name it 'fetchWeatherData'. Custom Actions are async Dart functions — they can await HTTP calls, read device storage, call Firebase, or do anything that requires waiting. In the code editor that appears, you will see a pre-generated function signature. The function receives typed parameters you define in the Arguments panel on the right, and returns a value you declare in the Return Value section. Add a String parameter named 'city'. Set the return type to String. Write your async logic inside the function body. Custom Actions are invoked from Action Flows — drag them from the Action palette onto a button's On Tap event.

fetch_weather_data.dart
1// Custom Action: fetchWeatherData
2// Arguments: city (String)
3// Return Value: String
4
5Future<String> fetchWeatherData(String city) async {
6 final response = await http.get(
7 Uri.parse(
8 'https://api.openweathermap.org/data/2.5/weather?q=$city&appid=YOUR_KEY',
9 ),
10 );
11
12 if (response.statusCode == 200) {
13 final data = jsonDecode(response.body);
14 final temp = data['main']['temp'];
15 final description = data['weather'][0]['description'];
16 return '$description, ${(temp - 273.15).toStringAsFixed(1)}°C';
17 } else {
18 return 'Could not fetch weather for $city';
19 }
20}

Expected result: A Custom Action named fetchWeatherData appears in the Custom Actions list with a green checkmark indicating no compile errors.

3

Create a Custom Function for Synchronous Transformations

Click '+' next to Custom Functions. Name it 'formatCurrency'. Custom Functions are SYNCHRONOUS pure Dart functions — they take inputs and immediately return an output with no waiting, no async, no await. They are ideal for formatting numbers, parsing strings, calculating values, and transforming data for display in widgets. In the editor, define your parameters (a double named 'amount' and a String named 'currencyCode') and a String return type. Write the pure transformation logic. Custom Functions appear in the Expression Editor throughout FlutterFlow — you can use them directly in widget Text bindings, Condition expressions, and anywhere you type a formula.

format_currency.dart
1// Custom Function: formatCurrency
2// Arguments: amount (double), currencyCode (String)
3// Return Value: String
4// IMPORTANT: No async/await — this is synchronous only
5
6String formatCurrency(double amount, String currencyCode) {
7 final symbols = {'USD': '\$', 'EUR': '€', 'GBP': '£', 'JPY': '¥'};
8 final symbol = symbols[currencyCode] ?? currencyCode;
9
10 if (amount >= 1000000) {
11 return '$symbol${(amount / 1000000).toStringAsFixed(1)}M';
12 } else if (amount >= 1000) {
13 return '$symbol${(amount / 1000).toStringAsFixed(1)}K';
14 }
15 return '$symbol${amount.toStringAsFixed(2)}';
16}

Expected result: The formatCurrency function appears under Custom Functions and is available in the Expression Editor when binding widget properties.

4

Create a Custom Widget for Flutter UI

Click '+' next to Custom Widgets. Name it 'GradientProgressBar'. Custom Widgets are full Flutter StatefulWidget or StatelessWidget classes. They appear as draggable components in the FlutterFlow widget palette, just like built-in widgets. Use them when you need UI that FlutterFlow's visual tools cannot produce — custom painters, complex animations, third-party package widgets, or any rendering logic requiring raw Flutter code. Define widget parameters in the Parameters panel on the right (e.g., a double named 'progress' and a Color named 'startColor'). These parameters become configurable Properties in the FlutterFlow UI when the widget is placed on a canvas.

gradient_progress_bar.dart
1// Custom Widget: GradientProgressBar
2// Parameters: progress (double, 0.0-1.0), startColor (Color), endColor (Color)
3
4class GradientProgressBar extends StatelessWidget {
5 final double progress;
6 final Color startColor;
7 final Color endColor;
8 final double height;
9
10 const GradientProgressBar({
11 Key? key,
12 required this.progress,
13 required this.startColor,
14 required this.endColor,
15 this.height = 12.0,
16 }) : super(key: key);
17
18 @override
19 Widget build(BuildContext context) {
20 return ClipRRect(
21 borderRadius: BorderRadius.circular(height / 2),
22 child: SizedBox(
23 height: height,
24 child: Stack(
25 children: [
26 Container(color: Colors.grey.shade200),
27 FractionallySizedBox(
28 widthFactor: progress.clamp(0.0, 1.0),
29 child: Container(
30 decoration: BoxDecoration(
31 gradient: LinearGradient(
32 colors: [startColor, endColor],
33 ),
34 ),
35 ),
36 ),
37 ],
38 ),
39 ),
40 );
41 }
42}

Expected result: The GradientProgressBar widget appears in the widget palette under Custom Widgets and can be dragged onto any page canvas.

5

Wire a Custom Action to a Button

Navigate to a page in your app. Select a Button widget on the canvas. In the right panel, click the Actions tab, then click '+' to add an action. In the Action Flow Editor, click '+' on the flow and search for your Custom Action by name — type 'fetchWeather' and it will appear under the Custom Actions section. Select it. FlutterFlow will prompt you to bind the 'city' argument — tap the binding icon and choose a TextField's value, an App State variable, or type a literal string. Set the Action Output Name (e.g., 'weatherResult') so you can reference the return value in subsequent actions. Add a second action: Update App State → set a variable to 'weatherResult'. Now a Text widget bound to that variable will update after the button tap.

Expected result: Tapping the button in the FlutterFlow preview triggers the Custom Action, and the Text widget updates with the returned weather string.

6

Test and Debug Custom Code

FlutterFlow compiles your Dart code in real time — red underlines in the editor mean syntax errors, and the panel shows a compile error banner. Fix all errors before leaving the Custom Code panel. For runtime debugging, use the FlutterFlow Run Mode (the play button, top-right) to test in the browser. For deeper debugging — print statements, breakpoints — click the '</> View Code' button (top-right) to open the full generated Flutter project, then run it in your local VS Code or Android Studio with a connected device. Custom Functions can also be tested inline: the Expression Editor shows a live preview of the return value when you type test inputs into the argument fields.

Expected result: No compile errors in the Custom Code panel, and the feature works end-to-end in Run Mode.

Complete working example

custom_code_examples.dart
1// ============================================================
2// FlutterFlow Custom Code — Three Types Demonstrated
3// ============================================================
4
5// --- 1. CUSTOM ACTION (async) ---
6// File: custom_actions/fetch_weather_data.dart
7// Use from: Action Flow Editor → Custom Actions
8
9Future<String> fetchWeatherData(String city) async {
10 final response = await http.get(
11 Uri.parse(
12 'https://api.openweathermap.org/data/2.5/weather?q=$city&appid=YOUR_KEY',
13 ),
14 );
15 if (response.statusCode == 200) {
16 final data = jsonDecode(response.body);
17 final temp = data['main']['temp'];
18 final desc = data['weather'][0]['description'];
19 return '$desc, ${(temp - 273.15).toStringAsFixed(1)}°C';
20 }
21 return 'Weather unavailable';
22}
23
24// --- 2. CUSTOM FUNCTION (synchronous only) ---
25// File: custom_functions/format_currency.dart
26// Use from: Expression Editor anywhere in FlutterFlow
27
28String formatCurrency(double amount, String currencyCode) {
29 final symbols = {'USD': '\$', 'EUR': '€', 'GBP': '£', 'JPY': '¥'};
30 final symbol = symbols[currencyCode] ?? currencyCode;
31 if (amount >= 1000000) {
32 return '$symbol${(amount / 1000000).toStringAsFixed(1)}M';
33 } else if (amount >= 1000) {
34 return '$symbol${(amount / 1000).toStringAsFixed(1)}K';
35 }
36 return '$symbol${amount.toStringAsFixed(2)}';
37}
38
39// --- 3. CUSTOM WIDGET (Flutter UI component) ---
40// File: custom_widgets/gradient_progress_bar.dart
41// Drag from the widget palette onto any page
42
43class GradientProgressBar extends StatelessWidget {
44 final double progress;
45 final Color startColor;
46 final Color endColor;
47 final double height;
48
49 const GradientProgressBar({
50 Key? key,
51 required this.progress,
52 required this.startColor,
53 required this.endColor,
54 this.height = 12.0,
55 }) : super(key: key);
56
57 @override
58 Widget build(BuildContext context) {
59 return ClipRRect(
60 borderRadius: BorderRadius.circular(height / 2),
61 child: SizedBox(
62 height: height,
63 child: Stack(
64 children: [
65 Container(color: Colors.grey.shade200),
66 FractionallySizedBox(
67 widthFactor: progress.clamp(0.0, 1.0),
68 child: Container(
69 decoration: BoxDecoration(
70 gradient: LinearGradient(
71 colors: [startColor, endColor],
72 ),
73 ),
74 ),
75 ),
76 ],
77 ),
78 ),
79 );
80 }
81}

Common mistakes when creating Custom Code in FlutterFlow

Why it's a problem: Writing async/await inside a Custom Function

How to avoid: If your logic needs to fetch data, call an API, or wait for anything, create a Custom Action instead. Move the async logic there, and use the Action Output to pass the result back to the UI via App State or Page State.

Why it's a problem: Declaring a Custom Widget with no default parameter values for optional parameters

How to avoid: Add sensible default values for all parameters that are not strictly required, for example: 'this.height = 12.0' and 'this.startColor = Colors.blue'. Mark only truly required parameters with the 'required' keyword.

Why it's a problem: Adding package imports in Custom Code without adding the package to pubspec dependencies

How to avoid: Go to Settings (gear icon, top-right) → Pubspec Dependencies → search for the package name (e.g., 'http') → toggle it on. The dependency is then available in all Custom Code files.

Why it's a problem: Using a Custom Action where a Custom Function would work

How to avoid: For pure data transformations (format a string, calculate a value, combine two strings), use a Custom Function. Its return value is available directly in the Expression Editor throughout the entire FlutterFlow UI.

Why it's a problem: Hardcoding API keys directly in Custom Action code

How to avoid: Store secrets in Firebase Remote Config or a backend environment variable. Call a Cloud Function that holds the key server-side, and have the Custom Action call that Cloud Function instead of the external API directly.

Best practices

  • Name Custom Actions with verb phrases (fetchUserProfile, sendPushNotification) and Custom Functions with noun phrases (formattedDate, userDisplayName) so their type is clear from usage.
  • Keep Custom Functions under 20 lines. If the logic is complex, break it into multiple focused functions rather than one large one.
  • Always handle null and error cases in Custom Actions — unhandled exceptions in async code crash the app silently in FlutterFlow Run Mode.
  • Use the Arguments panel to type-check Custom Action inputs rather than accepting dynamic/Object and casting inside the function body.
  • Test Custom Functions using the Expression Editor's inline preview before wiring them to a widget — type sample inputs to verify the output instantly.
  • Store sensitive configuration (API base URLs, keys) in App State constants or Firebase Remote Config, not as hardcoded strings in Custom Code.
  • Add a brief comment at the top of every Custom Code file explaining what it does and which widget or action flow uses it — FlutterFlow has no automatic documentation.
  • For Custom Widgets that use third-party packages, pin the package version in pubspec.yaml to avoid breaking changes when FlutterFlow auto-updates dependencies.

Still stuck?

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

ChatGPT Prompt

I'm building a FlutterFlow app and need to write a Custom Action in Dart that calls an external REST API and returns a parsed result. The function should handle errors gracefully and return a fallback string on failure. Show me a complete Custom Action template with the correct FlutterFlow function signature, error handling, and HTTP call.

FlutterFlow Prompt

Write a FlutterFlow Custom Function in Dart that takes a DateTime and returns a human-readable relative time string like '2 hours ago' or 'Yesterday'. It must be synchronous with no async/await.

Frequently asked questions

What is the difference between a Custom Action and a Custom Function in FlutterFlow?

Custom Actions are async Dart functions invoked from Action Flows (e.g., on button tap). They can await network calls, device APIs, and Firebase. Custom Functions are synchronous pure functions available in the Expression Editor for data transformation — they cannot use async/await. Use Actions for 'do something', Functions for 'transform something'.

Do I need to know Dart to use Custom Code in FlutterFlow?

Basic Dart syntax helps but is not mandatory for simple cases. FlutterFlow generates boilerplate for you, and most Custom Functions only require a return statement with straightforward logic. For complex Custom Widgets involving animations or custom painters, intermediate Dart/Flutter knowledge is recommended.

Can I use external Flutter packages in Custom Code?

Yes. Go to Settings → Pubspec Dependencies, search for the package (e.g., 'http', 'intl', 'fl_chart'), and enable it. The package is then importable in your Custom Code files. FlutterFlow Pro or higher is required to export and run the full project locally.

Why does my Custom Widget show a red error box on the FlutterFlow canvas?

This usually means the widget threw an error during construction — most commonly because a required parameter has no default value and FlutterFlow passes null when the widget is first placed. Add default values to all optional parameters. You can also check the error details by hovering over the red box.

Can Custom Functions access App State or Firestore?

No. Custom Functions are pure synchronous functions with no access to Flutter's context, FlutterFlow's app state, or any async data source. If you need to read App State or Firestore, use a Custom Action instead, which has access to the FFAppState singleton.

How do I pass data from a Custom Action back to the UI?

In the Action Flow Editor, set an 'Action Output' name when you configure the Custom Action. This output is available as a local variable in subsequent actions in the same flow. Use an 'Update App State' or 'Update Page State' action immediately after to store the value, then bind a widget to that state variable.

Can I use Custom Widgets on both mobile and web in FlutterFlow?

Yes, as long as the packages your Custom Widget uses support all platforms. Most Flutter UI packages are cross-platform. If you use a package that is mobile-only (e.g., camera or GPS), the widget will fail on web. Check pub.dev platform support badges before adding a package.

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.