• Flutter Times
  • Posts
  • Mastering State Management: Clean Architecture with BLoC

Mastering State Management: Clean Architecture with BLoC

A Comprehensive Guide to Building Scalable Flutter Applications

lib/
├── core/
│   ├── constants/
│   │   └── app_constants.dart                      # Application-wide constants (e.g., URLs, timeouts, etc.)
│   ├── data/
│   │   ├── datasource/
│   │   │   ├── local/                              # Sub-folder for local data sources
│   │   │   │   ├── database_manager.dart           # Manages SQLite or any local DB operations
│   │   │   │   ├── hive_services.dart              # Hive database-specific services
│   │   │   │   ├── sqlite_service.dart             # SQLite service implementations
│   │   │   │   ├── shared_preferences_service.dart # Shared preferences utilities
│   │   │   ├── remote/                             # Sub-folder for remote data sources
│   │   │   │   ├── authsource.dart                 # Manages remote user authentication (e.g., APIs)
│   │   │   │   ├── push_notification_data_source.dart # Manages push notification interactions with external services
│   │   ├── models/                                 # Contains data models for local/remote data
│   │   │   └── push_notification_model.dart        # Model for push notification data
│   │   └── repositories/
│   │       └── user_repository_impl.dart           # Implements user-related data operations (Auth, profile, etc.)
│   ├── domain/
│   │   ├── repositories/
│   │   │   └── user_repository.dart                # Defines user repository interface for dependency inversion
│   │   └── usecases/
│   │       └── global_usecase.dart                 # Application-wide use cases (e.g., app initialization)
│   ├── errors/
│   │   └── exception.dart                          # Custom exceptions for different error cases
│   │   └── failure.dart                            # Handles different failure states (e.g., ServerFailure, NetworkFailure)
│   ├── network/
│   │   └── network_info.dart                       # Utility to check network connectivity status
│   ├── theme/
│   │   └── style.dart                              # Contains global style definitions, such as color schemes, text themes
│   ├── widgets/                                    # Reusable UI components (e.g., buttons, text fields, etc.)
│   └── routes/
│       └── app_routes.dart                         # Centralized navigation logic and route definitions
├── features/
│   ├── register/                                   # Each feature should have its own sub-folder (register, login, etc.)
│   │   ├── domain/
│   │   │   └── usecases/
│   │   │       └── register_usecase.dart           # Feature-specific use cases for registration
│   │   ├── presentation/
│   │   │   ├── bloc/                               # State management using Bloc pattern
│   │   │   │   ├── register_bloc.dart              # Bloc logic for registration process
│   │   │   │   ├── register_event.dart             # Registration events
│   │   │   │   ├── register_state.dart             # Possible states (loading, success, error) during registration
│   │   │   └── register_screen.dart                # UI screen for user registration
├── utils/                                          # General utility functions, extensions, and helpers
│   ├── date_utils.dart                             # Utility functions for date handling
│   ├── string_utils.dart                           # Utility functions for string manipulations
│   └── validators.dart                             # Validation helpers for forms and inputs (e.g., email validation)

1. Core Layer

This layer contains essential services and utilities that the app depends on. It’s shared across the entire app and does not depend on any specific feature.

  • Constants: Global constants like API URLs and key configurations. Example: app_constants.dart.

  • Data: This handles raw data interactions, either from APIs or local storage.

    datasource/: Contains files related to external data sources such as APIs, local databases, shared preferences, or third-party libraries.

    . authsource.dart

    • Purpose: Manages user authentication methods, including signing up, logging in, and handling user sessions.

    • Example Functionality:

      • Email/password sign-up and sign-in

      • Token-based authentication

      • Firebase or third-party OAuth integrations

    . database.dart

    • Purpose: Handles the management of the local SQLite database, which can store structured data.

    • Example Functionality:

      • Create, read, update, and delete (CRUD) operations in SQLite.

      • Database schema management.

      • Database initialization and migration strategies.

    . database_manager.dart

    • Purpose: A higher-level database management class that coordinates different databases (e.g., SQLite, Hive, etc.) and handles database initialization.

    • Example Functionality:

      • Managing multiple database connections.

      • Handling database transactions.

      • General utilities for database operations like opening and closing connections.

    . hive_services.dart

    • Purpose: Provides services for managing key-value storage using Hive, a fast, lightweight, NoSQL database.

    • Example Functionality:

      • Saving and retrieving simple data like user preferences or settings.

      • Managing offline data storage for small, unstructured data sets.

    . push_notification_data_source.dart

    • Purpose: Manages the data related to sending and receiving push notifications.

    • Example Functionality:

      • Sending push notifications to users.

      • Receiving push notification data and handling actions (like opening a specific screen in the app).

      • Integrating with services like Firebase Cloud Messaging (FCM) or other notification services.

    . shared_preferences_service.dart

    • Purpose: Handles local storage of small pieces of data using SharedPreferences, a simple key-value store.

    • Example Functionality:

      • Saving user preferences like theme settings, language, or user login status.

      • Storing lightweight data that persists across app launches.

    . sqlite_service.dart

    • Purpose: Provides services for SQLite database operations, managing larger datasets locally in a structured manner.

    • Example Functionality:

      • Storing relational data like user records, posts, or product catalogs.

      • Executing SQL queries and managing relationships between tables.

      • Database indexing and query optimization.

    . storage.dart

    • Purpose: Manages session persistence, particularly using services like AWS Cognito for storing authentication tokens and user sessions.

    • Example Functionality:

      • Handling session management for authenticated users.

      • Using SharedPreferences or other local storage for persisting session data, like authentication tokens or refresh tokens.

    . user_service.dart

    • Purpose: Implements services related to user accounts, such as sign-up, login, and user profile management.

    • Example Functionality:

      • Signing up and logging in users via different authentication methods (e.g., email/password, OAuth, AWS Cognito).

      • Handling user data fetching, updating user profiles, and account management.

        models/ Folder

        • Purpose: Contains data models that define the structure of the data used in the application. These models help in organizing data consistently across different layers like data sources, services, and UI.

        push_notification_model.dart

        • Purpose: Defines the structure of a push notification object, used when sending, receiving, or managing notifications.

        • Example Fields:

          • title: The title of the notification.

          • message: The main body content of the notification.

          • timestamp: The time when the notification was received or sent.

          • isRead: A boolean flag indicating whether the notification has been read.

        • Usage:

          • This model could be used by the notification service (e.g., push_notification_data_source.dart) to represent incoming or outgoing notifications.

          • Helps in converting raw data from a push notification API (e.g., Firebase Cloud Messaging) into a structured format.

        repositories/ Folder

        • Purpose: The repository pattern is used to abstract the data layer from the rest of the application. It acts as a mediator between the data sources and the domain layer (use cases, business logic).

        user_repository_impl.dart

        • Purpose: This file contains the concrete implementation of the UserRepository interface, which handles all user-related data operations.

        • Functions:

          • Sign-up, Log-in: Methods to handle user registration and authentication.

          • Fetch User Profile: Retrieves user data from the data source (like a database or an API).

          • Save User Data: Stores user-related data to a local or remote data source (like SQLite, Firebase, or a REST API).

        • How it Works:

          • The repository communicates with various data sources (such as the database, remote APIs, or local storage) to fetch or store user data.

          • This implementation of UserRepository would interact with classes in the datasource/ folder (like authsource.dart or database.dart) to handle the actual data operations.

          • By having the repository pattern, the business logic layer (use cases) doesn’t need to know where the data comes from, making the app more modular and scalable.

  • domain/

    • Purpose: Contains the business logic and core rules of the application. It defines how data can be created, stored, and manipulated without considering how or where it is retrieved from.

      • repositories/

        • user_repository.dart

          • Purpose: Defines the contract (interface) that the Data layer must implement to handle user-related data operations.

          • Example Functionality:

            • Fetching user data from remote or local sources.

            • Signing in users via email and password.

            • Logging users out and managing authentication tokens.

      • usecases/

        • global_usecase.dart

          • Purpose: Contains business logic and specific operations that are application-wide and not tied to a specific feature.

          • Example Functionality:

            • A use case for refreshing authentication tokens.

            • Fetching global settings or preferences.

    errors/

    • Purpose: Contains all classes related to error handling, ensuring that exceptions and failures are properly managed.

      • exception.dart

        • Purpose: Defines custom exceptions used throughout the app, ensuring more readable and specific error handling.

        • Example Functionality:

          • Handling authentication exceptions, like invalid credentials.

          • Throwing exceptions for network failures.

      • failure.dart

        • Purpose: Implements a base Failure class to represent various error states in the app.

        • Example Functionality:

          • Handling server failures, validation failures, or data parsing errors.

          • Mapping exceptions into user-friendly error messages.

    network/

    • Purpose: Handles network-related operations, such as checking internet connectivity or managing network requests.

      • network_info.dart

        • Purpose: Provides utilities to check the current network state (online/offline).

        • Example Functionality:

          • Verifying if the user has an active internet connection before making API calls.

    theme/

    • Purpose: Manages the overall styling of the application, including themes, colors, and fonts.

      • style.dart

        • Purpose: Contains global styling configurations, including text styles, color schemes, and themes.

        • Example Functionality:

          • Defining a primary color palette for light and dark modes.

          • Setting default font sizes and button styles across the app.

    usecases/

    • Purpose: Contains use cases that implement specific, reusable business logic tied to various features of the application.

      • Example Functionality:

        • Creating a use case to manage user login.

        • Implementing a use case for fetching a list of products from an API.

    widgets/

    • Purpose: Contains reusable UI components that can be used across the app, avoiding code duplication.

      • Example Functionality:

        • A custom button widget that adapts to the app's theme.

        • A reusable text input widget with built-in validation.

    routes/

    • Purpose: Manages app navigation and defines all the routes that the app can navigate to.

      • app_routes.dart

        • Purpose: Contains the route definitions and paths used for navigating between screens.

        • Example Functionality:

          • Defining a route for the registration screen.

          • Managing navigation to different features of the app, like home, settings, or profile.

    2.Features/

    • Purpose: Contains feature-specific folders, each representing an individual part of the app (e.g., Login, Register). These include the domain, presentation, and data layers for that specific feature.

      • Register/

        • Purpose: Manages everything related to user registration, from business logic to UI.

        • Example Functionality:

          • register_bloc.dart: Handles state management for the registration feature.

          • register_event.dart: Defines all possible events during registration (e.g., entering email, submitting form).

          • register_state.dart: Defines the possible states of the registration process (e.g., loading, success, error).

          • register_screen.dart: The UI for user registration, containing input fields, buttons, and error messages.

    3. Utils/

    • Purpose: Contains utility functions and helpers used throughout the app.

      • Example Functionality:

        • Utility functions for formatting dates.

        • Helper methods for managing data conversions or input validations.

This structure allows for separation of concerns, making each layer independent. The core layer is reusable across different features, and the feature layers are isolated, ensuring modularity and scalability.