Mastering Clean Architecture with Cubit in Flutter (Ultimate Guide)

Flutter has revolutionized mobile app development, offering a single codebase for both Android and iOS platforms. But as projects grow, managing their complexity becomes a challenge. 

That’s where the concept of Clean Architecture comes in, ensuring your Flutter apps are scalable, maintainable, and testable. In this guide, we’re diving into how Cubit, a state management solution, seamlessly integrates with Clean Architecture to enhance your Flutter projects. 

Our aim is to demystify these concepts in easy steps, making them accessible even to those new to app development. 

Be with us on this journey to master Clean Architecture with Cubit in Flutter, making your Flutter app development process smoother and more efficient.

Basic Overview of Clean Architecture

Clean Architecture is a design philosophy that organizes a project into distinct layers, each with a specific responsibility, making the entire system more understandable, flexible, and robust. 

At its core, it emphasizes separating the concerns of software elements, allowing for independent management, development, and scaling of each component. 

Implementing Clean Architecture in Flutter projects offers a blueprint for building apps that are easier to maintain, adapt, and grow over time, regardless of the underlying platform or technology changes.

  • Clean Architecture allows Flutter apps to grow seamlessly, accommodating new features and user demands without disrupting the existing system.
  • By organizing code into well-defined layers, updates and bug fixes become more manageable, reducing the risk of introducing new errors.
  • Separate layers facilitate testing, as each component can be verified independently, ensuring higher quality and reliability of the app.

What Is Cubit?

Cubit is a streamlined, lightweight state management solution for Flutter apps, part of the bloc library package. It simplifies how you handle the app’s state changes, making it more predictable and manageable. 

Unlike other state management solutions that might require more boilerplate code or complex setups, Cubit offers a more straightforward approach. It focuses on managing state through simple classes called Cubits (or Blocs for more complex scenarios) that react to events and emit states.

What makes Cubit stand out?

Cubit truly shines in the Flutter ecosystem by striking an exceptional balance between simplicity and functionality. Unlike the Bloc pattern, which requires a detailed setup of events and states to manage application state changes, Cubit simplifies this process. 

It provides a more straightforward way to handle state without the extensive boilerplate code often associated with more complex state management solutions. This unique approach makes Cubit especially appealing to developers who aim for efficiency, enabling rapid development cycles without compromising on the application’s capability or performance.

Advantages of using Cubit in Flutter applications include:

  • Cubit’s simple API makes it accessible even to beginners, reducing the learning curve for state management in Flutter.
  • With Cubit, managing and updating the UI based on state changes is straightforward, leading to cleaner and more readable code.
  • It works seamlessly within the Flutter ecosystem, ensuring that integrating Cubit into your project enhances rather than complicates your development process.

Setting up Cubit in a Flutter project involves a few key steps:

  • Start by adding flutter_bloc to your pubspec.yaml file, which includes both Bloc and Cubit functionalities.
  • Define a Cubit class to manage the specific state of your application feature.
  • Use the BlocProvider widget to make your Cubit available to the widget tree.
  • Utilize the BlocBuilder or BlocListener widgets to build your UI based on the state changes emitted by your Cubit.

Integrating Cubit with Clean Architecture in Flutter

Understanding the integration of Clean Architecture in Flutter is easy process but first thing first you should understand the main layers of clean architecture which are:

  • Presentation Layer: Your app’s welcoming front desk, where UI meets user input.
  • Domain Layer: The operation’s brain, ensuring business logic and rules are on point.
  • Data Layer: The essential storage room for managing all things data.

How Cubit fits into the Clean Architecture layers?

Cubit is like the friendly concierge at the front desk (Presentation Layer), managing the state of the UI efficiently. It listens to user inputs, talks to the Domain Layer for instructions, and updates the UI accordingly. Its simplicity ensures that the front desk remains clutter-free and organized.

When Cubit receives a task from the Presentation Layer, it doesn’t act on its own. Instead, it consults with the Domain Layer, the manager, to decide the best course of action. This interaction ensures that business logic stays central, consistent, and unaffected by the UI.

Cubit also plays a crucial role in the Data Layer, overseeing the flow of data to and from the UI. It makes sure that data fetching, posting, and storage are all in sync with the user’s actions and the app’s state. Imagine Cubit as the diligent worker in the storage room, organizing and managing inventory so that everything is where it needs to be, exactly when it’s needed.

Implementing Clean Architecture with Cubit in Flutter

Implementing Clean Architecture with Cubit in Flutter involves a structured approach that enhances the scalability, maintainability, and overall quality of your app. 

Let’s walk through each step, mixing practical code examples with theory to ensure you get both the “how” and the “why.”

Setting up the Project Structure for Clean Architecture

First, organize your Flutter project into three main directories corresponding to the Clean Architecture layers: presentation, domain, and data. This structure keeps your project clean and navigable.


Creating the Domain Layer

The Domain Layer is the heart of your app’s business logic. It contains Entities and Use Cases.

Defining Entities

Entities are simple classes that represent the core business objects of your application.

class Product {
  final int id;
  final String name;
  final double price;

  Product({required, required, required this.price});

Writing Use Cases

Use Cases encapsulate and execute specific business rules. It’s a good practice to have one use case for each business action.

class GetProductsUseCase {
  final ProductRepository repository;


  Future<List<Product>> call() async {
    return await repository.getProducts();

Implementing the Data Layer

The Data Layer handles data transactions (CRUD operations) through repositories and data sources.


Repositories are interfaces that abstract the data layer from the rest of your app. Implement a repository for each entity.

abstract class ProductRepository {
  Future<List<Product>> getProducts();

Implement this interface in the Data Layer.

Data Sources (Local and Remote)

Data sources manage data from a specific source (e.g., API, local database).

class ProductRemoteDataSource {
  Future<List<Product>> fetchProducts() {
    // Implement API call

Building the Presentation Layer with Cubit

The Presentation Layer is where you connect your UI to the business logic using Cubit for state management.

State Management with Cubit

First, add flutter_bloc to your pubspec.yaml. Then, create a Cubit to manage the state related to a specific feature, like displaying products.

class ProductCubit extends Cubit<ProductState> {
  final GetProductsUseCase getProductsUseCase;

  ProductCubit(this.getProductsUseCase) : super(ProductInitial());

  void getProducts() async {
    try {
      final products = await getProductsUseCase();
    } catch (e) {

Connecting Cubit to UI Components

Use BlocProvider to make your Cubit available in the UI, and BlocBuilder to rebuild the widget tree in response to state changes.

  create: (context) => ProductCubit(getProductsUseCase),
  child: BlocBuilder<ProductCubit, ProductState>(
    builder: (context, state) {
      if (state is ProductLoading) {
        return CircularProgressIndicator();
      } else if (state is ProductLoaded) {
        return ListView.builder(
          itemCount: state.products.length,
          itemBuilder: (context, index) => ListTile(
            title: Text(state.products[index].name),
      } else {
        return Text('Something went wrong!');

Handling User Input and Displaying Data

Finally, manage user input by invoking Cubit methods to trigger state changes and update the UI accordingly.

  onPressed: () =><ProductCubit>().getProducts(),
  child: Icon(Icons.refresh),

Best Practices for Implementing Clean Architecture With Cubit in Flutter

Implementing Clean Architecture with Cubit in Flutter projects not only streamlines development but also enhances maintainability and scalability. To maximize these benefits, following certain best practices is crucial.

Organizing Files and Folders in a Clean Architecture Project

A well-organized project structure is fundamental. Divide your project into three main directories: presentation, domain, and data. Each of these should contain subdirectories relevant to their roles. For instance, the presentation layer could include screens, widgets, and cubits. This separation clarifies where specific types of code reside, making navigation and maintenance easier.

Naming Conventions for Readability and Maintainability

Adopting clear naming conventions is key. Names should intuitively convey the purpose of a class, function, or variable. For example, use descriptive names like ProductListCubit or FetchProductUseCase. This practice ensures that anyone new to the project can understand its structure and functionality at a glance.

Testing Strategies for Each Layer of Clean Architecture with Cubit

Testing is integral to the success of any project, and Clean Architecture makes it easier by separating concerns:

  • Presentation Layer: Focus on widget testing and ensuring that UI elements react correctly to Cubit states.
  • Domain Layer: Utilize unit tests for Use Cases and Entities to verify business logic independently of UI or data sources.
  • Data Layer: Implement mock tests for repositories and data sources to validate data handling and integration.

By testing each layer separately, you can pinpoint issues more accurately and maintain a robust codebase.

Use these 5 testing strategies in flutter clean architecture to enhance your app performance and reliability.

Error Handling and Exception Management

Effective error handling in clean architecture is crucial for a positive user experience. Within the Cubit, catch and handle exceptions, transitioning to error states when necessary. This approach allows the UI to respond gracefully to issues, such as displaying error messages or offering retry options. Furthermore, logging errors can aid in debugging and identifying recurring issues.

Implementing these best practices ensures that your Flutter project leveraging Clean Architecture and Cubit is not only well-structured and understandable but also robust and resilient to changes and errors.


The integration of Clean Architecture with Cubit in Flutter app development is not just a best practice but a transformative approach to building scalable, maintainable, and testable applications. By structuring our projects into distinct layers and employing Cubit for state management, we tap into a methodology that ensures our code is both robust and flexible. 

This combination allows developers to tackle complex projects with ease, facilitating a smooth development process and yielding high-quality applications. As we continue to optimize Flutter app development, embracing Clean Architecture and Cubit stands out as a key strategy for delivering exceptional user experiences and ensuring long-term project success. 

Through this guide, we’ve explored the foundations and practical steps to implement this approach, setting the stage for developers to build Flutter apps that are not only efficient but also adaptable to changing requirements and technologies.


Why do we use Cubit in Flutter?

Cubit is used in Flutter for its simplicity and effectiveness in managing state. It provides a straightforward way to handle state changes with less boilerplate code compared to other state management solutions, making it ideal for developers looking for an efficient and easy-to-understand approach.

Which is better, Cubit or BLoC in Flutter?

Whether Cubit or BLoC is better in Flutter depends on the specific needs of your project. Cubit offers a simpler, more straightforward approach to state management, which is great for smaller projects or those who prefer minimalism. BLoC, on the other hand, is more suitable for complex projects with extensive state management needs due to its more structured approach to handling events and states.

What is the difference between StateNotifier and Cubit?

StateNotifier and Cubit are both state management solutions, but they differ in their design and usage. StateNotifier is part of the river_pod package and uses a more functional-reactive style of programming. It’s designed to be used with the Provider package for state management. Cubit, part of the flutter_bloc package, focuses on a more simplified state management approach without the need for events, making it less complex and easier to implement for many developers.

Is Cubit the same as BLoC?

No, Cubit is not the same as BLoC, although they are related and part of the same package (flutter_bloc). Cubit is a simpler version of BLoC that doesn’t use events to trigger state changes. Instead, it exposes functions that can directly emit new states, making it easier to use and understand, especially for beginners or for simpler applications.

Why is Cubit not used anymore?

The statement that Cubit is not used anymore is inaccurate. Cubit is still widely used and actively supported as part of the flutter_bloc package. Its simplicity and ease of use make it a popular choice among Flutter developers, especially for projects where a full-fledged BLoC implementation is not necessary. Its continued use and support reflect its effectiveness in Flutter app development for managing state in a simpler, more direct manner.

Leave a Comment

Your email address will not be published. Required fields are marked *

Muhammad Ayaz

Muhammad Ayaz

Muhammad Ayaz is an SEO expert and tech content writer who turns complex tech topics into engaging content. While not a coder, his work is rigorously checked by Adnan Khan, Etechviral's senior developer, to ensure accuracy before it goes live.

Related Blogs

Converting JSON to Dart: The Ultimate Guide

In Flutter app development, seamlessly integrating JSON data into Dart code is crucial for building efficient, high-performing applications. This guide provides a comprehensive look at

Scroll to Top

Apply for Sales & Marketing

Company Description

eTechViral is a leading tech company with a team of skilled professionals specializing in developing customized software solutions from design to development. We prioritize client relationships, quality deliverables, and a compassionate exchange of energy.

Role & Responsibilities

This is a full-time remote role for a Sales and Marketing Specialist. The Sales and Marketing Specialist will be responsible for developing and implementing sales and marketing strategies, maintaining customer relationships, providing training to sales team members, and managing sales operations.

Role & Responsibilities

  • Excellent communication and customer service skills
  • Demonstrated ability to drive sales and meet quotas
  • Experience in sales management and operation
  • Ability to provide training to sales team members
  • Bachelor’s degree in Marketing, Business Administration or related field
  • Knowledge of virtual sales and marketing tools is a plus
  • Ability to work independently and remotely

Welcome to eTechviral