Skip to content
Tech Stack8 August 2026 · 7 min readUpdated 30 September 2026

FlutterFlow Custom Code: When to Drop to Pure Flutter in 2026

FlutterFlow custom code hits its limits at the Dart parser, not at your line count. The four signals that mean it is time to export, and what each paid plan really gates.

FlutterFlow Custom Code: When to Drop to Pure Flutter in 2026

What you'll learn

  • What Are FlutterFlow's Custom Code Limits?
  • Which FlutterFlow Plan Do You Need for Custom Code?
  • When Should You Drop to Pure Flutter?
Article contents

You have a FlutterFlow project, a Custom Action that almost works, and a growing suspicion that the builder is the thing slowing you down. Before you decide anything, open the Custom Code panel and count how many files you own there. That number tells you more than any feature grid.

FlutterFlow custom code has limits, but they are not where most people expect them. The builder happily lets you paste Dart into a Custom Action all afternoon. What it does not do is read that Dart the way the Dart analyzer reads it.

I maintain eight templates and more than 40 custom widgets on the official FlutterFlow Marketplace, so I spend real time on the seam where the visual builder stops and plain Flutter starts. This post covers what the limits are, which plan gates what, the four signals that mean you should leave, and the export detail that quietly overwrites your native config.

What Are FlutterFlow's Custom Code Limits?

A thick oak shape-sorter block on a sunlit desk with a rounded wooden peg wedged half-inside a square cut-out it is too large to pass through

FlutterFlow's practical limit is its Dart parser, not how much code you write: the builder scans your files to expose types in the visual editor, and shapes it cannot model are ignored.

There are several kinds of custom code, and they are not interchangeable. This table is the part I wish someone had shown me first.

TypeImportsReturnsWhat it is for
Custom FunctionOnly the default set of packages and filesA value, synchronouslyTransforms, formatters, validators
Custom Actionpub.dev packages allowedAlways a FutureAsync work, SDK calls, API glue
Custom Widgetpub.dev packages allowedA widget; talks back through callback actionsUI the widget palette cannot express

Per FlutterFlow's common code examples, Custom Functions cannot import new files or packages beyond the default dedicated imports, while Custom Widgets and Custom Actions can. The Custom Functions page adds that you cannot use FFAppState inside a function; you pass the value in as a parameter. So if your helper needs a package or app state, it is not a function. It is an action.

Code Files, the place for custom classes and enums, have stricter rules. FlutterFlow's Code File documentation lists three limitations: classes with generic types such as class ApiResponse<T> {} are not supported, methods or fields with function types are ignored, and Dart extensions are not supported yet. That rules out a generic result wrapper, an onTap callback field on a model, and extension StringX on String.

Those limits apply to what the parser has to model. Inside the body of a Custom Action you still write ordinary Dart. What you cannot do is hand those shapes back to the builder as types it understands.

What about custom widget size and previews?

Custom Widgets have one more rule that trips people up. The Custom Widgets documentation says width and height are mandatory, and that without them the widget will not render correctly. It also says the preview appears only once the widget compiles, so a broken import shows up as a missing preview rather than a friendly error.

Which FlutterFlow Plan Do You Need for Custom Code?

Four blank paper price tags strung on a taut thread up a beige wall, each hanging higher than the last and casting stair-stepped shadows

The Basic plan at $39 per month covers most custom code needs: the pricing page's comparison table marks code download, Custom Classes, third-party package imports and the CLI as available from Basic upward.

The FlutterFlow pricing page, read on 30 September 2026, is easy to misread. Custom Classes and CLI access can look like Business-tier features at $150 per month, but the comparison table does not say that. Here is what it says for the rows that matter here.

CapabilityFreeBasic ($39)Growth ($80, first seat)Business ($150, first seat)
Code ExtensibilityYesYesYesYes
Code download and APK downloadNoYesYesYes
Custom ClassesNoYesYesYes
Third-party package importsNoYesYesYes
CLINoYesYesYes
Push to GitHub, VS Code extensionNoNoYesYes

Business adds seats, more branches and features such as Figma frame import, none of which change what your Dart can do. One inconsistency is worth knowing: the Business plan card lists "CLI Access" as a highlight, while the comparison table marks the CLI from Basic. I trust the table because it is the itemised one, but check your own plan before you build a workflow around it.

That changes the question. A solo builder who needs custom classes or packages does not face a jump to $150. The pricing gate moves at Growth, and only for Git and editor tooling. Whether the export is worth it is a technical decision, and the price does not settle it for you. For a wider comparison of the two paths, see FlutterFlow vs Flutter for production apps.

For a team, the practical reading is simple. Growth is where collaboration tooling arrives, so a two-person team that wants pull requests and an editor extension pays for that step, not for the ability to write custom code. Price the plan against the workflow you need, and treat the export as a separate line item you can take at any tier that includes code download.

When Should You Drop to Pure Flutter?

A blank brushed-metal switch plate on a desk with four unmarked toggles, two standing upright and two lying flat, a frayed wire escaping beneath the edge

Drop to pure Flutter when two of these are true together: graph-shaped state, models that need generics, a native SDK in the critical path, or tests that must run in CI.

This is my recommendation, not a FlutterFlow rule. Any one signal is usually solvable inside the builder. Two compounding is where workarounds start costing more than the move. Here is each signal in concrete form.

  1. Your state is graph-shaped. App State and page state cover screens that read and write a handful of values. Once one change has to invalidate four unrelated views, you want Riverpod or Bloc holding that graph instead of a pile of Custom Actions poking at FFAppState. I compare those libraries in Flutter state management in 2026.
  2. Your models need generics. A Result<T> or a paginated Page<T> is the normal way to model an API layer in Dart. The Code File parser does not support generics.
  3. A native SDK sits in the critical path. Custom Podfile edits, entitlements or a hand-written platform channel mean you are working against generated native files, and that gets harder as the project grows.
  4. Tests are a requirement. You can write widget and integration tests against generated code. They are painful to keep green when a re-export rewrites the file underneath them.

Notice what is missing from that list: line count. Nothing on it says "more than a hundred Custom Actions". A project can carry a lot of custom code and stay healthy if each piece is a plain function or widget with clear inputs. The trouble starts when the pieces depend on each other through shared state that the builder cannot see.

Have you opened an exported project "just to check something" and still been in it three days later? That is signal number one arriving early.

How Do You Move Off FlutterFlow Without a Rewrite?

Move in stages: push logic into files you own while still in the builder, export once, then stop re-exporting so that only one side ever owns each file.

The migration that fails is the one where the builder and your editor both believe they own the same file. The ordered plan below avoids that.

  1. Move every non-trivial behaviour into Custom Actions and Custom Widgets while you are still in the builder. It feels like doing the work twice, but the exported project then contains your logic in files you named.
  2. Move business rules to the backend, whether that is Cloud Functions or an API you already run. Server-side logic survives any client rewrite.
  3. Export with the CLI and commit the result untouched. That gives you a diffable baseline.
  4. Make one small change in the builder, re-export and read the diff. That diff is a map of which files FlutterFlow believes it owns.
  5. Cut over screen by screen, replacing generated pages with hand-written ones behind your existing router. Nothing forces a big-bang switch.

Where does .flutterflowignore go?

It goes in the parent of your exported project folder, not inside it. FlutterFlow's export documentation gives the example: if the project is at /Users/yourname/projects/my_flutterflow_app/, the file lives at /Users/yourname/projects/.flutterflowignore, and each entry carries the project folder as a prefix, such as my_flutterflow_app/ios/Runner/Info.plist.

The same page says the file lists paths using globbing syntax and prevents those files from being overwritten during code export. This is my inference from that layout: an entry written the way you would write a .gitignore line, without the project folder prefix, is unlikely to match anything. Test it by exporting once and reading the diff, as in step four above.

Which pages help after the export?

If you are still choosing a direction, the alternatives to FlutterFlow worth considering are a better starting point than another feature grid. The Flutter developer tools I keep installed describe the post-export workflow, and what Flutter is covers the vocabulary if it is new.

Where would I draw the line?

If you are pre-launch and the app is mostly screens over a Firebase collection, stay in the builder. If you have paying users and a growing pile of Custom Actions, export, commit and treat FlutterFlow as a design tool that happens to emit Dart. That is a recommendation from my own work, and the number of Custom Actions that flips it will differ per team.

Open your project and count your Custom Actions and Code Files right now. Which of the four signals does your count point to?

Free resource

Free SaaS MVP Scope Template

A Notion document with the full feature checklist, MVP vs. nice-to-have table, pre-build questions, and cost signals — so you walk into any developer call knowing exactly what to ask for.

Get the template →
DL

Dusko Licanin

Full-Stack Developer · Banja Luka, Bosnia

Full-stack developer shipping SaaS MVPs, web apps, and mobile apps using AI-augmented workflows — without agency coordination overhead. Live portfolio: BookBed, Callidus, Pizzeria Bestek.

Frequently Asked Questions

What are FlutterFlow's custom code limits?

FlutterFlow's custom code limits come from its Dart parser rather than from how much code you write. The builder scans your files so it can expose your types in the visual editor, and shapes it cannot model are ignored. FlutterFlow's Code File documentation states that generic classes are not supported, methods or fields with function types are ignored, and Dart extensions are not supported yet. A second limit shows up early: Custom Functions cannot import packages beyond the default set and cannot use FFAppState directly, while Custom Actions and Custom Widgets can import pub.dev packages. If your helper needs a dependency, make it an action or a widget.

When should you leave FlutterFlow for pure Flutter?

Leave when at least two of these hold together: graph-shaped state, models that need generics, a native SDK in the critical path, or tests that must run in CI. This is a recommendation from experience rather than a FlutterFlow rule. Any single signal is usually workable inside the builder, and two compounding is where the workarounds start costing more than the move. Price is a weaker trigger than people assume: on the FlutterFlow pricing page checked on 30 September 2026, code download, Custom Classes, package imports and the CLI are marked available from the $39 Basic plan.

Is FlutterFlow or Flutter better for a production app?

Both ship production apps, and the split is that FlutterFlow is faster to a first version while plain Flutter is easier to maintain once a team, tests and native integrations arrive. FlutterFlow generates real Flutter code, so it is not a dead end; you export and keep going. What changes with size is who owns the files. Inside the builder the builder owns them, and a re-export can overwrite hand edits. In a plain Flutter repo, git owns them. If your app is mostly screens over a Firebase collection, staying in the builder is a reasonable call.

What can FlutterFlow custom widgets do that the builder cannot?

Custom Widgets let you write arbitrary Flutter UI and import pub.dev packages, which covers anything the visual widget palette cannot express. Per FlutterFlow's documentation they need an explicit width and height to render correctly, and they communicate outward through callback actions that you wire up where you place the widget. The preview only appears once the widget compiles successfully. Because a widget is Dart you own, it also travels with the code when you export, which makes widgets the safest place to put logic you might want after leaving the builder.

What is the FlutterFlow ceiling?

The FlutterFlow ceiling is the point where working around the builder costs more than leaving it, and it usually appears as a file ownership problem rather than a missing feature. You hand-edit a native file such as Info.plist, re-export, and the change disappears. FlutterFlow's export documentation describes a .flutterflowignore file for this, placed in the parent folder of your project with each entry prefixed by the project folder name. Export once, read the diff, and confirm your ignore entries actually match before you rely on them.