Recommended Project Structure

That is a great way to structure the codebase

Project Structure

What is a Feature ?

Features are not correlated with the Screens.

When we focus on the UI, we're likely to think of a feature as a single page or screen in the app.

That is wrong.

A Feature is not about what the user sees, but what the user does:

  • authenticate
  • manage the shopping cart
  • checkout
  • view all past orders
  • leave a review

Features should not depend on each other. They are built like a plugin.

Feature Folder Structure

The four layers of your feature.

Replace "example" with your Feature name.

Feature Folder Structure

Unidirectional Data Flow

Unidirectional data flow is a pattern where data moves in a single direction through your app: from data sources (repositories) → business logic (services) → state management (controllers) → UI.

Repository

Handles API calls, remote data connections with servers, local persistence etc.

Service

Can use repository to fetch, write or listen. Services can use one or more repositories.

Controller

Are handling the state of the UI. Controller can use Services.

Widgets

Should not have business logic or state logic and should be easy to understand and around 100-120 lines of code long. Always use stateless widgets instead of helper methods.

Riverpod

Riverpod serves as the glue between our layers. We use regular Providers for repositories and services. Services can also leverage StreamProviders and FutureProviders. The controller is implemented as an (Async)Notifier. The UI typically watches the controller's state and uses its methods. Additionally, the UI can directly watch StreamProviders if doing so reduces boilerplate code.

Error Handling

We generally use try-catch blocks in the Service Layer to manage errors. Where appropriate, we can also use AsyncValue.guard.

In the catch block, we utilize the logging class to provide useful feedback and track errors along with their stack traces.

The logging system is integrated with Sentry / Crashlytics, which helps understanding and fixing bugs in production and during QC testing.

We don't need to log unimportant exceptions like missing internet connections or incorrect password inputs—only those relevant to debugging should be tracked.

Learn More

I recommend this article for more details on Flutter architecture with Riverpod.