Improve your clean architecture on Flutter apps. Here is how!

Felipe Emídio
5 min readJan 16, 2023


How hard is it to clean the clean arch?

I’ve seen a lot of Flutter developers adopting Flutterando’s Clean Architecture Proposal. Well… this doesn’t surprise me, I’m one of them.

After working on several projects with clean arch within various teams, I noticed that adopting some not-spoken practices can enhance the development process.


Clean arch is a bunch of concepts presented in the Clean Code Book Series written by Robert C. (Uncle Bob) Martin. All hail the man!!

Image with all book covers of the Clean Code Serie
Clean Code Book Series by Robert C. Martin

At least the first book of this series (Clean Code) is a "must-read" book for all developers.

Flutterando's Proposal makes use of clean code concepts to generate scalable programming code on Flutter/Dart environment. Separating the system into 4 layers: Presenter, Domain, Infra, and External.

Clean arch layers with domain in the center
Layers for clean arch —Source: Flutterando's clean code proposal repository

It's not my point to explain the architecture. So, if more knowledge about it is needed, click here.

One controller per widget

On the presenter layer, we have widgets and controllers. Controllers are our widget state management class. And you want this separation to not gather interface render and interface logic.

Independent of the state management tool that you use, controllers should contain only information about the widget's state management.

The practice to highlight here is to avoid having more than one controller per widget.

A folder structure showing controllers in the same folder of their widget.
Keep the widget and controller always close to each other.

For example, in the initial stages of a project, usually you need a user registration form and a user editing form that have the same fields and rules.

The difference between these forms is the initial value of the field and the endpoint invoked. There is plenty of devs that would create a single controller with a flag for each situation here.

It sure makes the job done, but it's not enough for a professional. These forms can/tend to change their interfaces asymmetrically. Making us in a near future inflate our code, disrespecting the Single Responsibility Principle.

Flowchart showing the reuse of usecases but not of controller.
Flow chart of our example

Do you want me to use Ctrl+C Ctrl+V on my code? Well… yes! Avoid duplication is important, but also are SRP, readability, and maintainability. You need to balance the trade-offs.

Watch out! There are exceptions, for instance, if you're creating a category of widgets that must follow the same logic such as the AnimationController and the built-in implicit animation widgets.

Controllers only see what the widget needs

Continuing our talk about controllers, the clear arch splits the concepts of widget logic (Controllers) and business logic (Usecases).

That means that controllers should only have access to information that is pertinent to their widget.

For example, on a specific change password form there are just 2 fields. password and confirm the password. But our endpoint also needs the ID of the current user.

A change password page with 2 fields: new password and password confirmation.
Source: Pactto Mobile App

As this ID is not visible on the form, the controller should not know it. The getUserId logic has to be in the Usecase or in the Repository. But be sure of what you're doing.

Force error handling only on Usecases

To force the presenter layer to handle the failures scenarios, the Either class presented inside the Dartz lib or the Result class from result lib is recommended.

But on professional apps, a simple login operation can be executed with several steps. Such as:

  • Save login event on firebase analytics
  • Invoke login endpoint
  • Save the username locally to autofill the login form next time.
  • Save the authentication token locally to keep the user logged

This is our business logic so should be contained in the Usecase class. Failures can happen here. That means usecases can use the "throw" keyword.

Screenshot of a usecase code showing the use of the throw keyword.
Example code of a login function

If the Usecases dependencies, i.e. repositories and services, also return Either/Result values the error handling gonna mess with the readability, and the functions bigger than it needs.

Drivers are dangerous! Start with them.

Intuitively we should start developing a new feature on the domain layer. But worked better for me to keep the Fail Fast Principle in mind.

On developing a new feature… Where it is most likely to show the infeasibility of the new content? The answer is: where you don't have control.

You have less control over the Presenter and External layers.

The layers of the clean arch and who interacts the presenter and the external layer
Clean code layers

The Presenter is limited by the capacities of the Flutter framework, but with the great Google developers team and quick updates, it's quite reliable now, especially for mobile applications. Usually, we won't find anything that makes the project unfeasible here.

The External Layer is the communication with all libs and APIs our project needs. Most of its content is created by someone unknown and without supervision.

A lib that you want to add to your project can have out-of-date documentation and dependencies that conflict with your project.

Before starting a new feature, guarantee you have a functional driver. If something gonna be wrong that should happen on your first step.


Although the impressive contribution of Flutterando's Proposal for Flutter development doesn't mean we cannot improve even more.

We are the ones who know the necessities of our project. It's our responsibility to search for concepts, principles, and practices that deliver more value to our clients and teammates.

I just show some of them that worked for me. You shouldn't consider these as rules but as suggestions. Tell me if they make sense to you.

If you want to read more of my thoughts and programming tips, then check out my articles and follow or subscribe for upcoming content.



Felipe Emídio

Front-end developer searching for a better version of my code.