When FlutterFlow stops being the right tool
The signals repeat across projects. Custom Actions and Custom Functions start outnumbering visual logic — once a meaningful share of your behavior lives in escape-hatch Dart code, you're already writing Flutter, just inside a harness that makes it harder. Merge conflicts between team members editing the same page in the visual editor become routine. Performance work hits a wall because you can't restructure the generated widget tree — memoization, granular rebuilds, and custom render logic all assume control of the tree you don't have. And each FlutterFlow platform update can subtly change generated output, so your app's behavior shifts without a commit.
None of this means FlutterFlow was the wrong starting choice. Callidus exists because a clinic's FlutterFlow build had stalled — and the replacement shipped in about ten weeks precisely because the product was already validated. That's the pattern: FlutterFlow answers "should this product exist" cheaply; native Flutter answers "how does this product survive three years of feature requests". The rewrite decision is about which question you're currently asking.